-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Improve select control typescript types #60293
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
Improve select control typescript types #60293
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @AlchemyUnited. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
👋 Thanks for your first Pull Request and for helping build the future of Gutenberg and WordPress, @mikeybinns! In case you missed it, we'd love to have you join us in our Slack community. If you want to learn more about WordPress development in general, check out the Core Handbook full of helpful information. |
@mirka any chance you can take a look at this? I'd love to get this added if possible :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an excellent PR with a great description and test coverage (I've been starting to do static TS tests as well, so happy to see someone doing similar 😄), thank you!
That said, could you help me understand the use case for this? The types can already be accessed and be reused or overridden in any way, for example:
type SelectControlOption = NonNullable<
React.ComponentProps< typeof SelectControl >[ 'options' ]
>[ number ];
type MyNarrowOption = Omit< SelectControlOption, 'value' > & {
value: 'foo' | 'bar';
};
type MyNarrowSelectControlProps = Omit<
React.ComponentProps< typeof SelectControl >,
'options'
> & { options?: MyNarrowOption[] };
or then type check it somewhere in your consumer code:
const options: MyNarrowOption[] = [
{
label: 'Foo',
value: 'foo',
},
];
const myDefaultValue: MyNarrowOption[ 'value' ] = 'foo';
<SelectControl value={ myDefaultValue } options={ options } />;
Is it mostly about being able to do this in a more ergonomic way? If it's about ergonomics, my concern is that there are a ton of ways a consumer might want to narrow down some aspect of a component type, and it's not sustainable to cover every one of those possibilities by baking it into the wp-component as a generic. It might make sense when there is a very common use case, but this one for SelectControl seems really niche to me.
On the other hand, if there are good reasons other than ergonomics, then we might want to consider addressing this in a slightly more abstracted way, as it will likely be relevant to our other components as well.
Let me know what you think!
Thanks that's always good to hear 😄 So, yes, I will admit, this is mostly about ergonomics. The type library on DT used to have this generic before it was moved over to 1st party types and I really missed being able to quickly narrow down the type. With that being said, one of the major benefits of using a generic over other ways is that they can be automatically inferred by TypeScript. Take this example: <SelectControl
value="string"
options={ [
{
value: 'narrow',
label: 'Narrow',
},
{
value: 'value',
label: 'Value',
},
] }
/>; TypeScript can automatically infer from the generic that the only options should be "narrow" and "value", so "string" is invalid. The end user has just caught a bug without having to add any extra code on their end, and they may have not even realised it was a bug until TypeScript warns them about it. It think because of this automatic inference, it's worth adding, and that the ability for users to add value types manually is just a side benefit. |
I'll also note that the QueryControls bug I found is a good real world example of why this is useful, even if the bug was minor in severity. But to play devils advocate against myself, the TimePicker "bug" I found was not really a bug in the code, just a typescript loose type and may produce errors for users if the type of value is looser than the type of the generic. 🤷 |
@mikeybinns - Thanks for taking care of this. |
Thank you for clarifying, that was very helpful! Just wanted to let you know that this is on my todo list, as I want to think about it a bit more and take into consideration some other components that might benefit from this kind of type checking. I appreciate your patience 🙏 |
Apologies for the long delay! This became outdated due to TypeScript version updates (5.1 to 5.4), so I went ahead and did a 5.4-compliant version at #64069, simplifying things in a few spots as well. It would be great if you could take a look and let me know what you think. |
Superseded by #64069 |
What?
Allow optional narrowing of the SelectControl component for better type validation.
Why?
This is an enhancement to the type of the SelectControl. It will help users of Typescript narrow down their types to prevent errors. It also infers types if options is provided which may flag code errors with some implementations. There is no change to the base JS code, this is only a typescript change.
This generic type was previously available in the 3rd party types package, but was removed when components moved to 1st party types.
Fixes #16060
How?
I have made the type for the SelectControl component generic, which allows users to explicitly provide an option type, or infers that type from the provided options array.
If you have provided an
options
array but don't want the narrower type, you can specify<string>
as the generic to re-loosen this type.In adding this enhancement, there are two implementations with
@wordpress/components
which needed to be updated.TimePicker
in date-time/time which was using a select for the month. I have added the necessary type to tighten the type here.QueryControls
inquery-controls
which actually had a bug in its implementation. Theundefined
states were not being accounted for in the string interpolation, leading potentially toundefined/undefined
as the value. This would not affect runtime as the browser knows that option doesn't exist and defaults to the first option, but I have fixed the bug to satisfy typescript.Testing Instructions
I have included a file to add type-related tests, and added each possible test case. For testing, see that file (
select-control/types-test.tsx
). Future changes which break implementation would throw a type error in that file, preventing linting from passing.Implementation examples taken from
types-test.tsx