Nickofthyme
Nickofthyme

Reputation: 4327

Extract react component prop types with generics

Is there a way to use react's ComponentProps type with generic-typed components?

For example:

function MyComponent<T extends string, N extends number>({ text, number }: { text: T; number: N }) {
  return (
    <div>
      {text} - {number}
    </div>
  );
}

export type MyComponentProps = ComponentProps<typeof MyComponent>;

I expect MyComponentProps to be something like...

type MyComponentPropsExpected<T extends string, N extends number> = {
    text: T;
    number: N;
}

With the generics preserved. However, the type just drops the generic and fills all generics with their extends type. In this case the actual MyComponentProps type is...

type MyComponentProps = {
    text: string;
    number: number;
}

See demo here

Upvotes: 2

Views: 1106

Answers (1)

Wing
Wing

Reputation: 9701

Problem

MyComponent has two type parameters T and N with each constrained to string and number respectively:

function MyComponent<T extends string, N extends number>(...) { ... }

However when the MyComponentProps type is created, no types are passed into those parameters:

export type MyComponentProps = ComponentProps<typeof MyComponent>;
//                                                   ^^^^^^^^^^^ 
//                                           No type arguments passed in

As a result, the resultant type is the type of the constrained component props.

Solution

TypeScript >=4.7

You'll need to pass in types to MyComponent:

type MyComponentProps = ComponentProps<typeof MyComponent<"text", 2>>;

TypeScript Playground

However, this syntax is only available in TypeScript 4.7+, when instantiation expressions were introduced.

TypeScript <4.7

Prior to TypeScript 4.7, I would have pulled out the props into its own interface and used that instead of building a type from ComponentProps:

interface MyComponentProps<T extends string, N extends number> {
  text: T;
  number: N;
}

function MyComponent<T extends string, N extends number>({
  text,
  number
}: MyComponentProps<T, N>) {
  ...
}

export const test2: MyComponentProps<"text", 2> = {
  text: "text",
  number: 2
};

TypeScript Playground

Further reading

Upvotes: 1

Related Questions