Skip to content

Generic type 'ControlProps' requires 2 type argument(s). TS2314 #4308

Closed

Description

@cseas

Are you reporting a bug or runtime error?

Yes, but in TypeScript.

Description

I can't figure out what in the react-select changelog caused this but upgrading to react-select@3.1.1 has broken my Chromatic deployments so I'm assuming the type of something changed.

Here's the error:
Generic type 'ControlProps' requires 2 type argument(s). TS2314

And the code that caused it:

export const controlStyles = (base: CSSProperties, state: ControlProps<{}>) => ({
  ...base,
  height: "3.2rem"
})

This error is actually originating from a lot of types I've used from @types/react-select including NoticeProps, OptionProps, ValueType, etc. I'm assuming it's because of the upgrade because with react-select@3.1.0, everything worked smoothly.

Feel free to look at the logs of the errors for more details or the source.

Activity

  1. Methuselah96 commented on Dec 2, 2020

    @Methuselah96
    Collaborator

    These errors originate from a recent change in @types/react-select. Most of these types now take a second generic parameter (IsMulti) that specifies whether isMulti is set to true. In most cases, you should be safe setting that to boolean if you want to allow isMulti to be either true or false.

    Let me know if you have any more questions about how to deal with the new IsMulti generic parameter. Also, if you have an issue with the TypeScript types in the future, the DefinitelyTyped repo is probably a better place to create the issue. Just make sure to tag me and I'll get around to taking a look at it.

  2. prescottbreeden commented on Dec 2, 2020

    @prescottbreeden

    Running into the same issue as well but with ValueType, been using this package for over a year on a client's project and it broke all of our pipelines. In the future you guys should make type changes like that a major version upgrade not a minor so it doesn't break builds.

  3. Methuselah96 commented on Dec 2, 2020

    @Methuselah96
    Collaborator

    @prescottbreeden This is due to a change in @types/react-select hosted on DefinitelyTyped (not this repository). @types package versions do not follow SemVer so you should pin your package versions to an exact package version to avoid these sorts of problems in the future. Unfortunately breaking changes in minor/patch versions of @types packages are inherent to @types and there's no way around this problem (that I know of).

    If you have any issues with the new types, let us know. We're only looking to improve the types, so feedback would be appreciated if that's not the case.

  4. prescottbreeden commented on Dec 2, 2020

    @prescottbreeden

    I see, that makes sense, thanks for the extra info

  5. cseas commented on Dec 2, 2020

    @cseas
    Author

    Okay, so we put boolean as a second argument. And what do we do with the error in isMulti?

    <MySelect
      isMulti
    />

    now gives the following error:
    Type 'true' is not assignable to type 'false | undefined'.ts(2322) Select.d.ts(141, 3): The expected type comes from property 'isMulti' which is declared here on type 'IntrinsicAttributes & SearchProps'

  6. Methuselah96 commented on Dec 2, 2020

    @Methuselah96
    Collaborator

    If you have a custom Select component (in this case MySelect), you'll likely have to add IsMulti as a second generic parameter (i.e., IsMulti extends boolean) to the type of MySelect and pass IsMulti along to the Select component within MySelect.

    If you need more specific help than that, you'd have to show me the code for MySelect.

  7. cseas commented on Dec 2, 2020

    @cseas
    Author

    Yeah, the source is here.

    The type of custom Select component extends { Props } from "react-select"

    I'm a bit confused. What is IsMulti and why is it not a part of Props from react-select? Sorry, I'm not really good with generics.

  8. prescottbreeden commented on Dec 2, 2020

    @prescottbreeden

    I took the low road and rolled back my types and react-select to 3.0.8

  9. Methuselah96 commented on Dec 2, 2020

    @Methuselah96
    Collaborator

    @cseas The easiest way to fix your case is to change

    export interface SearchProps extends Props {
    

    to

    export interface SearchProps extends Props<OptionTypeBase, boolean> {
    

    (you'll have to import OptionTypeBase from react-select).

    Although I will warn you that your types aren't actually safe, since you're not propagating the generics. So there are potential bugs that the types should be catching for you, but won't be able to because you're essentially throwing away the generics.

  10. Methuselah96 commented on Dec 2, 2020

    @Methuselah96
    Collaborator

    @prescottbreeden Just so you know, you shouldn't have to roll react-select itself back. Just pinning @types/react-select to @types/react-select@3.0.26 should be sufficient.

  11. Methuselah96 commented on Dec 2, 2020

    @Methuselah96
    Collaborator

    @cseas

    What is IsMulti

    The IsMulti generic makes it so that we can correctly narrow ValueType in various scenarios. Previously ValueType looked like this:

    type ValueType<OptionType extends OptionTypeBase> = OptionType | OptionType[] | null | undefined;
    

    Now it looks like this:

    type ValueType<OptionType extends OptionTypeBase, IsMulti extends boolean> =  (IsMulti extends true ? OptionType[] : OptionType) | null | undefined;
    

    What does this mean? It means that if we're dealing with a single-select component (IsMulti is false) then we know that ValueType will not be an array. But if we know that we're dealing with a multi-select component (IsMulti is true) then we know that ValueType will be an array.

    A practical example (see this CodeSandbox for the full code) where this makes a difference is the callback function passed into the onChange prop which provides an argument of type ValueType<OptionType> as it's first parameter. Previously code like this would result in a type error:

    export default class App extends React.Component {
      onChange = (newValue: ValueType<OptionType>) => {
        if (newValue != null) {
          // TS error: "Property 'label' does not exist on type 'OptionType | OptionType[]'."
          // To fix this we would have to cast `newValue` to `OptionType`.
          console.log(newValue.label);
        }
      };
    
      render() {
        return <Select options={colourOptions} onChange={this.onChange} />;
      }
    }
    

    However we know that it's not an array because this isn't a multi-select component and we shouldn't have to cast. Therefore we can now set the second generic parameter to false to specify that this is not a multi-select component and now it compiles correctly:

      onChange = (newValue: ValueType<OptionType, false>) => {
    

    why is it not a part of Props from react-select

    It is part of the Props type, it's just that it defaults to false (and is therefore optional). If you don't provide it, and you don't set the isMulti prop to true, it will set it to false and assume you're dealing with a single-select component.

  12. cseas commented on Dec 2, 2020

    @cseas
    Author

    Thanks a lot for the help and explanation!
    I was able to make it work.
    For anyone looking for reference, here's how I fixed the breaking types.

    And yeah, I know the types could use some improvements. But that's mostly just because I'm not familiar with generics and just wanted to make react-select "work" in TypeScript with some sort of type checking.

    It would be really great if there was some sort of implementation guide or reference available for how to properly use react-select with TypeScript. But unfortunately, the best I could find was scattered information across GitHub issues and StackOverflow answers.

    I kinda started this repository to serve as an implementation reference for react-select with TypeScript and that eventually became this component.

  13. Methuselah96 commented on Dec 2, 2020

    @Methuselah96
    Collaborator

    @cseas Looks like you the use case I provided above is actually pretty common in your code base. I made a PR to fix the types in your project so that you don't have to cast in onChange anymore.

  14. SebVory commented on May 6, 2021

    @SebVory

    These errors originate from a recent change in @types/react-select. Most of these types now take a second generic parameter (IsMulti) that specifies whether isMulti is set to true. In most cases, you should be safe setting that to boolean if you want to allow isMulti to be either true or false.

    Let me know if you have any more questions about how to deal with the new IsMulti generic parameter. Also, if you have an issue with the TypeScript types in the future, the DefinitelyTyped repo is probably a better place to create the issue. Just make sure to tag me and I'll get around to taking a look at it.

    How about making the isMulti attribute optional? You would not break the codebases and usually people wants selects to have just single option but mainly for the first reason.

  15. Methuselah96 commented on May 7, 2021

    @Methuselah96
    Collaborator

    Yeah, there are more places where it could be optional, feel free to make a PR on DefinitelyTyped. We recently merged a TypeScript conversion PR in the main code base though (#4489) which we plan to release as react-select@5. In these types I've made most of the external-facing types have optional IsMulti generics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions