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

[material-ui][Autocomplete] Type issue once wrapped with styled() #21727

Open
2 tasks done
hiahmedhameed opened this issue Jul 9, 2020 · 17 comments
Open
2 tasks done
Labels
component: autocomplete This is the name of the generic UI component, not the React module! docs Improvements or additions to the documentation ready to take Help wanted. Guidance available. There is a high chance the change will be accepted typescript

Comments

@hiahmedhameed
Copy link

hiahmedhameed commented Jul 9, 2020

I'm trying to use Autocomplete with styled-components in typescript environment. But when using the component, linter start to complain about styled autocomplete component. I checked for this issue but not sure if it is duplicate. Could not find something related?

  • The issue is present in the latest release.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Current Behavior 😯

No overload matches this call.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<(<T, Multiple extends boolean | undefined = undefined, DisableClearable extends boolean | undefined = undefined, FreeSolo extends boolean | undefined = undefined>(props: AutocompleteProps<T, Multiple, DisableClearable, FreeSolo>) => Element), DefaultTheme, {}, never>): ReactElement<...>', gave the following error.
    Type '(_event: React.ChangeEvent<{}>, value: OptionData | null) => void | undefined' is not assignable to type '(event: ChangeEvent<{}>, value: unknown, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<unknown> | undefined) => void'.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<(<T, Multiple extends boolean | undefined = undefined, DisableClearable extends boolean | undefined = undefined, FreeSolo extends boolean | undefined = undefined>(props: AutocompleteProps<T, Multiple, DisableClearable, FreeSolo>) => Element), DefaultTheme, {}, never>): ReactElement<...>', gave the following error.
    Type '(data: OptionData) => string' is not assignable to type '(option: unknown) => string'.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<(<T, Multiple extends boolean | undefined = undefined, DisableClearable extends boolean | undefined = undefined, FreeSolo extends boolean | undefined = undefined>(props: AutocompleteProps<T, Multiple, DisableClearable, FreeSolo>) => Element), DefaultTheme, {}, never>): ReactElement<...>', gave the following error.
    Type '(option: OptionData) => boolean' is not assignable to type '(option: unknown, value: unknown) => boolean'.  TS2769
     62 |   return (
     63 |     <StyledFormControl>
  > 64 |       <StyledAutocomplete
       |       ^
    65 |         id={id}
    66 |         options={options}
    67 |         // classes={classes}

I started to add one prop by another then i realised the following:

<StyledAutocomplete
        id={id}
        options={options}                                                                          // type of OptionData[]
        onChange={(_event: React.ChangeEvent<{}>, value) =>        // <<== if i set value: OptionData | null linter will complain
          onChange && onChange(value as OptionData | null)
        }
        value={currentValue}
        getOptionLabel={data => (data as OptionData).label}             // <<== if i set data: OptionData | null linter will complain
        getOptionSelected={option =>                                                // <<== if i set option: OptionData | null linter will complain
          (option as OptionData).label === currentValue?.label
        }
        autoHighlight
        renderInput={params => (
          <TextField
            {...params}
            error={!!error}
            name={name}
            label={label}
            variant="standard"
          />
        )}
      />

Expected Behavior 🤔

Would it be possible to take T type of the options property and pass it throw all function that working with the same type ?

Steps to Reproduce 🕹

Simply styled Autocomplete with styled-components.

[...]

const StyledAutocomplete = styled(Autocomplete)`
  & .MuiInputBase-root {
    height: 4.1rem;
  }
  & label: {
    fontsize: 2rem;
  }
`;

[....]


<StyledAutocomplete
        id={id}
        options={options}
        // onChange={(_event: React.ChangeEvent<{}>, value: OptionData | null) =>                 // <<== Linter error
        //   onChange && onChange(value)
        // }
        value={currentValue}
        // getOptionLabel={(data: OptionData) => data.label}         // <<== Linter error
        // getOptionSelected={(option: OptionData) =>.                  // <<== Linter error
        //   option.label === currentValue?.label
        // }
        autoHighlight
        renderInput={params => (
          <TextField
            {...params}
            error={!!error}
            name={name}
            label={label}
            variant="standard"
          />
        )}
      />

Context 🔦

As consistency we use styled-components as a generic way of styling. We need to use the same here. The styling suggestion works fine using makeStyles

Your Environment 🌎

Tech Version
Material-UI v4.9.12
React 16.13.1
Browser Chrome 83.0.4103.116
TypeScript 3.8.0
OS MacOS.
@hiahmedhameed hiahmedhameed added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Jul 9, 2020
@oliviertassinari oliviertassinari added the component: autocomplete This is the name of the generic UI component, not the React module! label Jul 9, 2020
@oliviertassinari
Copy link
Member

This makes me think of #19842. Do you have a reproduction?

@oliviertassinari oliviertassinari added status: waiting for author Issue with insufficient information and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Jul 9, 2020
@oliviertassinari

This comment has been minimized.

@hiahmedhameed
Copy link
Author

hiahmedhameed commented Jul 13, 2020

Hi @oliviertassinari
Sorry for taking long preparing sandbox sample.

TBH not sure how the types working in sandbox ? But I still believe we have kind of the same issue.

Please checkout this link:
https://codesandbox.io/s/strange-borg-wetsr

The any type in arguments at least should be {} from options type. If you may note that i used cascading type like (data as AutocompleteOptionData).label which i think make no sense with typescript. Just because the argument taking type of unknown.

Hope that can help.

@oliviertassinari oliviertassinari added typescript and removed status: waiting for author Issue with insufficient information labels Jul 13, 2020
@oliviertassinari
Copy link
Member

Thanks for the reproduction, would this help https://codesandbox.io/s/pedantic-tharp-bdwj8?file=/src/Autocomplete.tsx?

@hiahmedhameed
Copy link
Author

That's actually a solution but dose that considered as permanent solution ?

Also why styled-components dose not extend the props internally ? This is odd when I have to do the extending myself right ?

What do you think ?

@oliviertassinari
Copy link
Member

I'm not aware of a better solution, but I'm no expert on TypeScript either. The concern seems something we would at the very least to document.

@oliviertassinari oliviertassinari added docs Improvements or additions to the documentation ready to take Help wanted. Guidance available. There is a high chance the change will be accepted labels Jul 14, 2020
@hiahmedhameed
Copy link
Author

@oliviertassinari

Just to give an update that might help as input.

I tried to apply your solution in my local code, I'm getting linter error under Autocomplete2

function StyledAutocomplete<
  T,
  Multiple extends boolean | undefined,
  DisableClearable extends boolean | undefined,
  FreeSolo extends boolean | undefined
>(props: AutocompleteProps<T, Multiple, DisableClearable, FreeSolo>) {
  return <Autocomplete2 {...props} />;
}

Linter error said

No overload matches this call.
  Overload 1 of 2, '(props: Pick<Pick<AutocompleteProps<unknown, boolean | undefined, boolean | undefined, boolean | undefined>, "classes" | "loading" | "defaultValue" | "onChange" | ... 307 more ... | "innerRef"> & Partial<...>, "classes" | ... 310 more ... | "innerRef"> & { ...; } & { ...; }): ReactElement<...>', gave the following error.
    Type '{ ChipProps?: object | undefined; closeIcon?: ReactNode; clearText?: string | undefined; closeText?: string | undefined; disabled?: boolean | undefined; disablePortal?: boolean | undefined; ... 305 more ...; ref?: ((instance: unknown) => void) | ... 2 more ... | undefined; }' is not assignable to type 'Pick<Pick<AutocompleteProps<unknown, boolean | undefined, boolean | undefined, boolean | undefined>, "classes" | "loading" | "defaultValue" | "onChange" | ... 307 more ... | "innerRef"> & Partial<...>, "classes" | ... 310 more ... | "innerRef">'.
      Types of property 'onChange' are incompatible.
        Type '((event: ChangeEvent<{}>, value: Value<T, Multiple, DisableClearable, FreeSolo>, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<...> | undefined) => void) | undefined' is not assignable to type '((event: ChangeEvent<{}>, value: unknown, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<unknown> | undefined) => void) | undefined'.
          Type '(event: ChangeEvent<{}>, value: Value<T, Multiple, DisableClearable, FreeSolo>, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<...> | undefined) => void' is not assignable to type '(event: ChangeEvent<{}>, value: unknown, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<unknown> | undefined) => void'.
            Types of parameters 'value' and 'value' are incompatible.
              Type 'unknown' is not assignable to type 'Value<T, Multiple, DisableClearable, FreeSolo>'.
  Overload 2 of 2, '(props: StyledComponentPropsWithAs<(<T, Multiple extends boolean | undefined = undefined, DisableClearable extends boolean | undefined = undefined, FreeSolo extends boolean | undefined = undefined>(props: AutocompleteProps<T, Multiple, DisableClearable, FreeSolo>) => Element), DefaultTheme, {}, never>): ReactElement<...>', gave the following error.
    Type '{ ChipProps?: object | undefined; closeIcon?: ReactNode; clearText?: string | undefined; closeText?: string | undefined; disabled?: boolean | undefined; disablePortal?: boolean | undefined; ... 305 more ...; ref?: ((instance: unknown) => void) | ... 2 more ... | undefined; }' is not assignable to type 'Pick<Pick<AutocompleteProps<unknown, boolean | undefined, boolean | undefined, boolean | undefined>, "classes" | "loading" | "defaultValue" | "onChange" | ... 307 more ... | "innerRef"> & Partial<...>, "classes" | ... 310 more ... | "innerRef">'.
      Types of property 'onChange' are incompatible.
        Type '((event: ChangeEvent<{}>, value: Value<T, Multiple, DisableClearable, FreeSolo>, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<...> | undefined) => void) | undefined' is not assignable to type '((event: ChangeEvent<{}>, value: unknown, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<unknown> | undefined) => void) | undefined'.
          Type '(event: ChangeEvent<{}>, value: Value<T, Multiple, DisableClearable, FreeSolo>, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<...> | undefined) => void' is not assignable to type '(event: ChangeEvent<{}>, value: unknown, reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<unknown> | undefined) => void'.ts(2769)

The interesting thing is in this line with my local code

onChange={(_event: React.ChangeEvent<{}>, value) => { ... }).  // <<== When hover on `value` i can see "(parameter) value: string | AutocompleteOptionData | (string | AutocompleteOptionData)[] | null"

Note that other events argument in getOptionLabel, getOptionSelected has been solved with the right type of AutocompleteOptionData .

@oliviertassinari oliviertassinari removed docs Improvements or additions to the documentation ready to take Help wanted. Guidance available. There is a high chance the change will be accepted labels Jul 17, 2020
@vinikatyal
Copy link

Did u make this work ?

@hiahmedhameed
Copy link
Author

@vinikatyal

I'm casting argument types as follow:

[...]

interface AutocompleteOptionData {
  value: string;
  label: string;
}

[...]

<StyledAutocomplete
        id={id}
        options={options}
        onChange={(_event: React.ChangeEvent<{}>, value) =>
          onChange &&
          onChange(
            (value as AutocompleteOptionData | null) || {
              label: '',
              value: '',
            }
          )
        }
        value={currentValue}
        getOptionLabel={data => (data as AutocompleteOptionData).label}
        getOptionSelected={option =>
          (option as AutocompleteOptionData).label === currentValue?.label
        }
        renderInput={params => (
          <TextField
            {...params}
            error={!!error}
            name={name}
            label={label}
            variant="standard"
          />
        )}
      />

[...]

TBH, Autocomplete is not easy component but i understand that is designed to be generic.

@vinikatyal
Copy link

    onChange={(_event: React.ChangeEvent<{}>, value) =>
          onChange &&
          onChange(
            (value as AutocompleteOptionData | null) || {
              label: '',
              value: '',
            }
          )
        }

Thanks this is good I used something similar as well 👍

@andrei9669
Copy link

andrei9669 commented Nov 19, 2020

#21727 (comment)

Casting does remove the errors but still feels like a hack.

@oliviertassinari
Copy link
Member

The approach in https://codesandbox.io/s/pedantic-tharp-bdwj8?file=/src/Autocomplete.tsx seems to work, I think that we can go with the docs.

@oliviertassinari oliviertassinari added docs Improvements or additions to the documentation ready to take Help wanted. Guidance available. There is a high chance the change will be accepted labels Jun 27, 2021
@enheit
Copy link

enheit commented Jul 14, 2021

I'm not sure if this topic is related to my issue but it looks very similar.

I use styled from @material-ui/core to wrap Autocomplete. After using it inside my component all options are set to unknown. See picture

image

Styles look like this

import { styled, Autocomplete as AutocompleteOriginal } from '@material-ui/core'

export const Autocomplete = styled(AutocompleteOriginal)(props => ({
  // styles go here
}))

If I use Autocomplete component directly from the @material-ui/core the options types are correct.

P.S. I use @material-ui/core: 5.0.0-beta.0

@oliviertassinari oliviertassinari changed the title Style Autocomplete component with styled-components [Autocomplete] Type issue once wrapped with styled() Jul 14, 2021
@oliviertassinari
Copy link
Member

@enheit Yes, it's the same issue. I have updated #21727 (comment) to better function.

@oliviertassinari oliviertassinari added OCD21 ready to take Help wanted. Guidance available. There is a high chance the change will be accepted and removed ready to take Help wanted. Guidance available. There is a high chance the change will be accepted labels Jul 14, 2021
@mnajdova mnajdova removed the OCD21 label Aug 24, 2021
@ortonomy
Copy link

The approach in https://codesandbox.io/s/pedantic-tharp-bdwj8?file=/src/Autocomplete.tsx seems to work, I think that we can go with the docs.

@oliviertassinari -- there are two approaches here and comments indicate the second doesn't work -- which approach exactly where you proposing?

1 or 2?

@NunchakusLei
Copy link

I found the following code make it works for me. Not sure if it will help or not since I'm using MUI v5 and not the version you mentioned. Post it here in case it helps,

const StyledAutocomplete = styled(Autocomplete)`
  & .MuiInputBase-root {
    height: 4.1rem;
  }
  & label: {
    fontsize: 2rem;
  }
` as typeof Autocomplete;

@samuelsycamore
Copy link
Member

Revisiting this in Q4 '23, it looks like we could close this issue by documenting @NunchakusLei's solution in the Autocomplete doc.

@samuelsycamore samuelsycamore changed the title [Autocomplete] Type issue once wrapped with styled() [material-ui][Autocomplete] Type issue once wrapped with styled() Nov 28, 2023
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! docs Improvements or additions to the documentation ready to take Help wanted. Guidance available. There is a high chance the change will be accepted typescript
Projects
None yet
Development

No branches or pull requests

9 participants