Skip to content

[ui] Improve typechecking on qs usage #29083

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

hellendag
Copy link
Member

@hellendag hellendag commented Apr 7, 2025

Summary & Motivation

Following the recent JS error involving parsed querystrings crashing the page, try to lock down types on our qs usage in useQueryPersistedState and similar hooks that make use of encode/decode behavior.

This PR makes use of the qs.ParsedQs type, which is the type returned by qs.parse itself. This replaces our current {[key: string]: any} type, which at present leaves us with no real recourse for typechecking our parsed values.

Now, encode/decode functions will have to be much more careful about the types involved. For instance, in the case of the error hotfixed by #28791, the decode function would now have to refine the value for qs['open-nodes'] in order to satisfy the Set<string> type in its return value. If qs['open-nodes'] were an object (as in the error case), the new Set(...) would fail typechecking, and the developer would be forced to provide it an array of strings extracted from the qs argument.

How I Tested These Changes

TS, lint, jest.

Copy link
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link

github-actions bot commented Apr 7, 2025

Deploy preview for dagit-storybook ready!

✅ Preview
https://dagit-storybook-oxxywd1wb-elementl.vercel.app
https://dish-fe-842-try-to-improve.components-storybook.dagster-docs.io

Built with commit 76467fa.
This pull request is being automatically deployed with vercel-action

Copy link

github-actions bot commented Apr 7, 2025

Deploy preview for dagit-core-storybook ready!

✅ Preview
https://dagit-core-storybook-fsdoo8qhw-elementl.vercel.app
https://dish-fe-842-try-to-improve.core-storybook.dagster-docs.io

Built with commit 76467fa.
This pull request is being automatically deployed with vercel-action

@hellendag hellendag requested review from bengotow and salazarm April 7, 2025 21:51
@hellendag hellendag marked this pull request as ready for review April 7, 2025 21:51
@@ -28,10 +28,10 @@ interface ActiveSuggestionInfo {
idx: number;
}

export interface TokenizingFieldValue {
export type TokenizingFieldValue = {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a few changes like this to ensure that the type arguments specified for useQueryPersistedState etc can satisfy QueryPersistedDataType.

if (Array.isArray(openNodes)) {
return new Set(openNodes.map((node) => String(node)));
}
return new Set();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is our bug case. We now explicitly check that the value provided via qs is an array, and cast the contents to string to match the expected return type.

@@ -239,7 +244,7 @@ describe('useQueryPersistedState', () => {
});
await userEvent.click(screen.getByText(`{"enableA":false,"enableB":true}`));
await waitFor(() => {
expect(querySearch).toEqual('');
expect(querySearch).toEqual('?enableA=true&enableB=false');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this was expecting empty string before...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this is interesting actually - I think the reason is because the values were being set back to the default specified on like 225, and default values were omitted from the query string. I wonder if the defaults are sometimes used as the query-ified form and sometimes used as the state-form, so changing them to false above broke this?

| string
| undefined
| number
| boolean
| null;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is to more or less match the recursiveness of what qs is able to encode, and clean up these any types.

Copy link
Contributor

@salazarm salazarm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@hellendag hellendag force-pushed the dish/fe-842-try-to-improve branch from 9c71c96 to 35d82c0 Compare April 10, 2025 15:38
[INTERNAL_BRANCH=dish/qs-in-cloud]
@hellendag hellendag force-pushed the dish/fe-842-try-to-improve branch from 35d82c0 to 76467fa Compare April 10, 2025 15:59
Copy link
Collaborator

@bengotow bengotow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh this is great, I am definitely guilty of not thinking of the string[] vs string problem...

@@ -239,7 +244,7 @@ describe('useQueryPersistedState', () => {
});
await userEvent.click(screen.getByText(`{"enableA":false,"enableB":true}`));
await waitFor(() => {
expect(querySearch).toEqual('');
expect(querySearch).toEqual('?enableA=true&enableB=false');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this is interesting actually - I think the reason is because the values were being set back to the default specified on like 225, and default values were omitted from the query string. I wonder if the defaults are sometimes used as the query-ified form and sometimes used as the state-form, so changing them to false above broke this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants