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

V5: Custom selectProps require ts-ignore in Typescript #4804

Closed
jmrossy opened this issue Sep 25, 2021 · 16 comments · Fixed by #5044
Closed

V5: Custom selectProps require ts-ignore in Typescript #4804

jmrossy opened this issue Sep 25, 2021 · 16 comments · Fixed by #5044
Labels
category/documentation Issues or PRs about documentation or the website itself

Comments

@jmrossy
Copy link

jmrossy commented Sep 25, 2021

Hi, congrats on the big v5 release!

After upgrading to v5, use of custom props on Select cause errors in Typescript. They still seem to work but require ts-ignore directive to silence the error.

Example:

    <Select<MyOption>
      id={id}
      options={options}
     {... more props ...}
      components={{ SingleValue }}
      myCustomProp='foobar'
    />

function SingleValue({ children, ...props }: SingleValueProps<MyOption>) {
  return (
      <div>
        <label htmlFor={selectProps.name}>
          {props.selectProps.myCustomProp}
        </label>
         <components.SingleValue {...props}>{children}</components.SingleValue>
         <div className="ml-1">
            <components.DownChevron />
         </div>
      </div>)
}
@Methuselah96 Methuselah96 added the issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet label Sep 25, 2021
@Methuselah96 Methuselah96 self-assigned this Sep 25, 2021
@Methuselah96 Methuselah96 added category/documentation Issues or PRs about documentation or the website itself and removed issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet labels Sep 26, 2021
@Methuselah96
Copy link
Collaborator

Methuselah96 commented Sep 26, 2021

Sorry, I forgot to include how to type custom selectProps in the upgrade guide and TypeScript usage documentation. I'll work on adding that documentation.

The only reason this worked for @types/react-select is because there was an index signature that would type any unknown prop as any. This was a little bit too loose and was causing numerous problems. In order to do this in v5 you'll need to use module augmentation to augment the type of the Select props. Here's how to update your example so it works in v5:

import Select, { components, GroupBase, SingleValueProps } from 'react-select';

declare module 'react-select/dist/declarations/src/Select' {
  export interface Props<
    Option,
    IsMulti extends boolean,
    Group extends GroupBase<Option>
  > {
    myCustomProp: string;
  }
}

function SingleValue({
  children,
  ...props
}: SingleValueProps<MyOption, false>) {
  return (
    <div>
      <label htmlFor={props.selectProps.name}>
        {props.selectProps.myCustomProp}
      </label>
      <components.SingleValue {...props}>{children}</components.SingleValue>
      <div className="ml-1">
        <components.DownChevron />
      </div>
    </div>
  );
}

export default function App() {
  return (
    <Select<MyOption>
      id={id}
      options={options}
      components={{ SingleValue }}
      myCustomProp="foobar"
    />
  );
}

Let me know if that works for you.

@Methuselah96 Methuselah96 added the awaiting-author-response Issues or PRs waiting for more information from the author label Sep 26, 2021
@Methuselah96 Methuselah96 removed their assignment Sep 26, 2021
@jmrossy
Copy link
Author

jmrossy commented Sep 26, 2021

@Methuselah96 Works for me! Thanks for the response :)

@Methuselah96 Methuselah96 removed the awaiting-author-response Issues or PRs waiting for more information from the author label Sep 26, 2021
@vjee
Copy link
Contributor

vjee commented Oct 18, 2021

Is there any way to make this work with multiple components?
Our application has multiple select components that all pass different props to Select to be used in custom child components.
The solution above is global and would require optional props to make it work but that's not very clean.

Thanks in advance.

@Methuselah96
Copy link
Collaborator

No, unfortunately there's no way to specify it per component. Note there also wasn't a way to do this for @types/react-select either. If you want to go back to that behavior you can just augment the props with an index signature that allows any prop.

I've looked into trying to make it so that the custom props could be another generic on the Select component, but it made the types much more complex and I think I had trouble getting it working well. Feel free to give it a try yourself and we'd be happy to merge a PR if it doesn't add too much complexity.

@doubleforte
Copy link

declare module 'react-select/dist/declarations/src/Select' {
  export interface Props<
    Option,
    IsMulti extends boolean,
    Group extends GroupBase<Option>
  > {
    myCustomProp: string;
  }
}

I tried adding the declare to my react-app-env.d.ts file and the app crashes because it can't find my custom prop. If I have it in a separate file it works just fine. Any idea why this wouldn't work in react-app-env.d.ts?

@Methuselah96
Copy link
Collaborator

No, not off-hand. Are you sure react-app-env.d.ts is being included in the TypeScript compilation? That's the only thing I can think of. Also, I assume you're importing GroupBase in react-app-env.d.ts?

@doubleforte
Copy link

Yes, importing GroupBase.

Seems that there was a conflict with another declare module I had in that file. I was able to find a way around declaring in react-app-env.d.ts and now everything is fine. Thanks. :)

@eckmLJE
Copy link

eckmLJE commented Feb 2, 2022

Is this in the docs yet? I was looking for it but couldn't find it. Glad I was able to find this here! It would be nice to be able to reference docs for the rest of my team that may come across this in the future vs. an issue here.

@Methuselah96
Copy link
Collaborator

Sorry that I've neglected to add this to the documentation earlier. I've created #5044 to add it to the TypeScript docs and upgrade guides and can now also be found in the release notes for v5.0.0.

@gxxcastillo
Copy link

Hi @Methuselah96 , I have different use cases for react-select within my app and when I use module augmentation the result is that now every instance of react-select is being required to comply to the new augmented props. Is there a way to work around this?

@vjee
Copy link
Contributor

vjee commented Mar 25, 2022

@gxxcastillo See #4804 (comment)

@mdmathewdc
Copy link

If you are using an external file for defining the typings, use this:

Inside app/typings/react-select.d.ts:

// eslint-disable-next-line import/no-extraneous-dependencies
import { GroupBase } from 'react-select';

declare module 'react-select/dist/declarations/src/Select' {
  export interface Props<
    Option = unknown,
    IsMulti extends boolean = false,
    Group extends GroupBase<Option> = GroupBase<Option>,
  > {
    option?: Option;
    isMulti: IsMulti;
    group?: Group;
    myCustomProp?: boolean;
  }
}

@bostrom
Copy link

bostrom commented Apr 6, 2023

Also, I assume you're importing GroupBase in react-app-env.d.ts?

This just bit me, too. Although it's kind of obvious, the import could be included in the example in the docs for copy-pasters like me, since even eslint didn't seem to notice it 😉

@nifanic
Copy link

nifanic commented Jul 14, 2023

I managed to augment Select.d.ts (to add custom isValid) by importing Prop, like so—

/* eslint-disable @typescript-eslint/no-unused-vars */
import { Props } from 'react-select/dist/declarations/src/Select';

/**
 * @see https://react-select.com/typescript#custom-select-props
 */
declare module 'react-select/dist/declarations/src/Select' {
  import { GroupBase } from 'react-select';

  export interface Props<
    Option,
    IsMulti extends boolean,
    Group extends GroupBase<Option>
  > {
    /**
     * `true` if value is valid.
     */
    isValid?: boolean;
  }
}

@eckmLJE
Copy link

eckmLJE commented Nov 28, 2023

The docs for this issue appear to have changed, and since updating react-select and using what is shown in the docs in my component library, consumers of my library are no longer getting my custom props, although they are available within the library. Previously, consumers were able to pick up these custom prop types fine.

In my Select component's index in the library:

import type {} from 'react-select/base';

// https://react-select.com/typescript#custom-select-props

declare module 'react-select/base' {
  export interface Props<
    Option,
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    IsMulti extends boolean,
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    Group extends GroupBase<Option>
  > {
    appearance?: SelectAppearance;
    isSortable?: boolean;
    status?: InputValidationStatus;
    maxSelectedOptions?: number;
  }
}

And in the consumer:
screenshot of code editor showing a typescript error for the custom prop that states the prop does not exist on select props

Edit: after experimenting with npm link locally, I was able to fix the issue by reverting to the previous declaration path, while keeping the 5.8 version:

declare module 'react-select/dist/declarations/src/Select' {
  export interface Props<
    Option,
    // Must include these generics, but they are unused in the added custom props
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    IsMulti extends boolean = false,
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    Group extends GroupBase<Option> = GroupBase<Option>
  > {
    appearance?: SelectAppearance;
    isSortable?: boolean;
    status?: InputValidationStatus;
    maxSelectedOptions?: number;
  }
}

Is it possible there is something about the new recommended module path in the docs that does not get picked up by library consumers properly?

@blerner
Copy link

blerner commented Dec 2, 2023

I found a very hacky, unsound workaround for now:

const Group: React.FC<GroupProps<MyCustomOptions>> = (props) => {
  const {
    selectProps,
  } = props;
  const {
    myCustomProp,
  } = (selectProps as unknown as {
    myCustomProp: ...
  });
  return <CustomComponent myCustomProp={myCustomProp} />
};

///// later, to use it
      <Select
        // Pass-along props to CustomComponent
        // Must use this spread-of-lieral-object shenanigan to
        // dodge TS's complaining about unknown properties
        {... { myCustomProp: "some value" }}
      />

As far as I can tell, the custom props get passed along just fine; I just needed to hide them from the overzealous type-checker here :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category/documentation Issues or PRs about documentation or the website itself
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants