i-like-robots / react-tag-autocomplete

⚛️ A simple, accessible, tagging component ready to drop into your React projects (new repo)
https://i-like-robots.github.io/react-tag-autocomplete/
ISC License
178 stars 12 forks source link

Allow generic type for suggestions that extends Tag #13

Closed tarngerine closed 2 years ago

tarngerine commented 2 years ago

Currently Tag is expected to have the keys value and label. I'd love to have Typescript recognize additional metadata that I supply with it, e.g. having the suggestions prop be typed as <T extends Tag>?

Thank you!

i-like-robots commented 2 years ago

This sounds like a good idea @tarngerine but I actually don't know how to achieve this, would you be happy to raise a PR with the suggested changes? I'd be very happy to accept contributions.

I imagine it would look something like this?:

export type Tag<T = void> = T & {
  label: string
  value: string | number | symbol | null
}

export type TagSuggestion<T = void> = Tag<T> & Partial<TagMetaProps>

export type TagSelected<T = void> = Tag<T>
tarngerine commented 2 years ago

Here's a codesandbox with a minimal example with suggestions/suggestionTransform, where suggestionTransform is getting the extended type provided via suggestions

https://codesandbox.io/s/pedantic-nash-c21wkm?file=/src/App.tsx

the general idea is that you specify <T extends Tag> and use that + T in the component, props, and any type that unions/extends the base type Tag instead of Tag itself

export type Tag = {
  label: string;
  value: string | number | symbol | null;
};

export type TagMetaProps = {
  disabled: boolean;
};

// We use T instead of Tag here, the generic ensures it extends Tag
export type TagSuggestion<T extends Tag> = T & Partial<TagMetaProps>;

// The T is shared with other props
interface Props<T extends Tag> {
  suggestions: TagSuggestion<T>[];
  suggestionsTransform: (
    value: string,
    suggestions: TagSuggestion<T>[]
  ) => TagSuggestion<T>[];
}

// Lastly we have T extends Tag at the top component level since it needs to be provided to the Props type
export function ReactTags<T extends Tag>(props: Props<T>) {
  return (
    <>
      Check the code editor to see if suggestionsTransform has the extended type
    </>
  );
}
tarngerine commented 2 years ago

I took a stab locally but it's turning out a bit non trivial due to some limitations of generic constraints, will poke around and see what i can do

tarngerine commented 2 years ago

Yea I might call this, it requires touching so much of the code it doesn't seem worth it. I'll push up a PR as a demo but I wasn't able to fully get it working — the type becomes overly constrained and I'm not sure when that happens/ don't have time to resolve it.

i-like-robots commented 2 years ago

Thank you for your time and effort investigating @tarngerine, I really appreciate it and I've learned some things from your sample code. I've made a suggestion in the PR which I know will cover at least some cases but I'll continue to think about it.

i-like-robots commented 2 years ago

I've exported the TagSelected and TagSuggestion types as part of the public interface for the component in d8c0f62cee2336985b53b40d2fda9b31dd73bbf8 and added some examples to the readme. I've tried this in a medium sized project and it works as expected.