Skip to content
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

[Autocomplete] Improve typings #18854

Merged
merged 10 commits into from
Jan 11, 2020
Merged

[Autocomplete] Improve typings #18854

merged 10 commits into from
Jan 11, 2020

Conversation

blasterpistol
Copy link
Contributor

@blasterpistol blasterpistol commented Dec 15, 2019

Closes #18810

  • Basic types
  • Handle a situation when a value is an array
  • Pass right types to other components and hooks

@blasterpistol
Copy link
Contributor Author

blasterpistol commented Dec 15, 2019

It remains to solve the situation when the value is an array.
It can be solved by this helper type:

type MultipleOrNot<T> =
  | { multiple: true; value?: T[]; defaultValue?: T[], ... }
  | { multiple: false; value?: T; defaultValue?: T, ... };
  | { multiple?: undefined; value?: T; defaultValue?: T, ... };

...

props: UseAutocompleteProps<T> & MultipleOrNot<T>

Passing array in default value throws type error when multiple: false:
image

And everything ok when pass multiple: true:
image

@mui-pr-bot
Copy link

mui-pr-bot commented Dec 15, 2019

No bundle size changes comparing e5b2e22...f66e47b

Generated by 🚫 dangerJS against f66e47b

@oliviertassinari
Copy link
Member

@testarossaaaaa How is the exploration of the problem going?

@blasterpistol
Copy link
Contributor Author

@oliviertassinari on vacation now, on the 10th I can finish it. Types can already be used, if I'm not mistaken, they cover 80% of cases. As I see there was an error on CI side.

@oliviertassinari
Copy link
Member

@testarossaaaaa Awesome, I have looked a bit at the test fails, it should be green. However, I have noticed this new message during the prop-types generation:

Unable to handle node of type "ts.TypeFlags.IncludesStructuredOrInstantiable", using any

@oliviertassinari oliviertassinari added the component: autocomplete This is the name of the generic UI component, not the React module! label Jan 7, 2020
@oliviertassinari oliviertassinari marked this pull request as ready for review January 7, 2020 23:14
@oliviertassinari
Copy link
Member

@testarossaaaaa Alright, I have tried to clean up the demos, it would be awesome if you could double-check :).

@@ -62,7 +62,7 @@ export default function Playground() {
{...defaultProps}
id="controlled-demo"
value={value}
onChange={(event, newValue) => {
onChange={(event: any, newValue: FilmOptionType | null) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Typescript can't discriminate union type without specifying the multiple prop, it's why we need to write concrete type here, an another solution for typescript users is settingmultiple={false} or multiple={undefined} so typescript can infer right typings for onChange handler, what way do you prefer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems like a bug in typescript microsoft/TypeScript#35769

Copy link
Member

Choose a reason for hiding this comment

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

Great to know. Should we specify the multiple prop (which will be closer to real life usage)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that we can explicitly write multiple={undefined} in typescript examples (with a mention in the documentation why this is needed), and when the bug is fixed, this will not be a breaking change. Or strictly require to specify multiple as true or false, but it seems that this is not a great DX.

Copy link
Member

Choose a reason for hiding this comment

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

Just to be clear, it's only needed because we spread {...defaultProps}? What if we duplicate the props instead?

Copy link
Member

Choose a reason for hiding this comment

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

OK thanks, in this can, I think that it would be better not to change the current tradeoff. The usage of the TypeScript demo is at 10% (and growing). It would look weird to see undefined. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If a typescript developer has not defined the multiple prop as false or undefined than all other dependent props will also receive invalid typings, not only the onChange prop.

Copy link
Member

Choose a reason for hiding this comment

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

What about we document it in the ## Limitations section of the page for TypeScript users, with a link to the related TypeScript issue?

Copy link
Contributor Author

@blasterpistol blasterpistol Jan 10, 2020

Choose a reason for hiding this comment

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

Sounds good. So we explicitly provide typings for onChange methods, like in this line of code above?

Copy link
Member

Choose a reason for hiding this comment

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

This would be my proposal, I believe we already get significant improvements with your changes. This is a roadblock that will require more effort.

@oliviertassinari oliviertassinari merged commit 2a872f8 into mui:master Jan 11, 2020
@oliviertassinari
Copy link
Member

Thank you for pushing this further, I hope you had great holidays :).

@kelly-tock
Copy link

I believe this was the change that is causing an issue for my project. thoughts?

export interface AutocompleteProps
  extends Pick<MuiAutocompleteProps, Exclude<keyof MuiAutocompleteProps, 'renderInput'>> {
  autofocus?: boolean;
  label?: string;
  renderInput?(params: RenderInputParams): JSX.Element;
  TextFieldProps?: Optional<TextFieldProps>;
}

I upgraded from 4.0.0-alpha.39 to 4.0.0-alpha.42, and it started saying I need a type parameter. ok, cool.

for the time being, I tried

export interface AutocompleteProps
  extends Pick<MuiAutocompleteProps<any>, Exclude<keyof MuiAutocompleteProps<any>, 'renderInput'>> {
  autofocus?: boolean;
  label?: string;
  renderInput?(params: RenderInputParams): JSX.Element;
  TextFieldProps?: Optional<TextFieldProps>;
}

but then I get in a place where we're using the component:

<Autocomplete
          options={citiesAvailable}
          getOptionLabel={(option: Option) => _.toString(option.label)}
          onChange={handleSelectCity}
          onClose={() => setShowSearch(false)}
        />

and says

Type '{ autofocus: true; options: { key: number; value: IMetroArea; label: string; }[]; getOptionLabel: (option: Option) => string; onChange: (changeEvent: ChangeEvent<{}>, selectedCityOption: { key: string; label: string; value: MetroArea; } | null) => void; onClose: () => void; }' is not assignable to type 'IntrinsicAttributes & AutocompleteProps'.
Property 'onChange' does not exist on type 'IntrinsicAttributes & AutocompleteProps'.

and in a different spot:

<Autocomplete
      label="Your location"
      classes={{
        noOptions: hasLoadedSuccessfully ? undefined : 'LocationSuggest_options-noSearchTerm',
      }}
      options={placesOptions}
      getOptionLabel={(option: Option) => _.toString(option.label)}
      defaultValue={
        selectedPlace && {
          key: selectedPlace.place_id,
          value: selectedPlace.place_id,
          label: selectedPlace.description,
        }
      }
      TextFieldProps={{ value: locationSearch, onChange: onSearchChange }}
      onChange={onSelect}
      onClose={onClose}
    />

Type '{ label: string; classes: { noOptions: string | undefined; }; options: SelectOption[] | undefined; getOptionLabel: (option: Option) => string; defaultValue: { key: string; value: string; label: string; } | undefined; TextFieldProps: { value: string | undefined; onChange: (searchChange: ChangeEvent) => void; }; onChange: (event: ChangeEvent<{}>, value: Option | null) => void; onClose: () => void; }' is not assignable to type 'IntrinsicAttributes & AutocompleteProps'.
Property 'defaultValue' does not exist on type 'IntrinsicAttributes & AutocompleteProps'.

any ideas on this?

what's interesting to me is :

export interface AutocompleteProps<T>
  extends UseAutocompleteCommonProps<T>,
    StandardProps<
      React.HTMLAttributes<HTMLDivElement>,
      AutocompleteClassKey,
      'defaultValue' | 'onChange' | 'children'
    > {

where those 2 are in the removals part of the standard props interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: autocomplete This is the name of the generic UI component, not the React module! typescript
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Autocomplete] Reduce types duplication
4 participants