React and Typescript: How to extend generic component props?

hendra

Imagine a flexible component that takes a React.ComponentType and its props and renders it:

type Props<C> = {
  component: React.ComponentType<C>;
  componentProps: C;
  otherProp: string;
};

const MyComponent = <C extends {}>(props: Props<C>) => {
  return React.createElement(props.component, props.componentProps);
};

Can I somehow let MyComponent receive the dynamic props directly, e.g. like that (not working):

type Props<C> = {
  component: React.ComponentType<C>;
  otherProp: string;
};

const MyComponent = <C extends {}>(props: Props<C> & C) => {
  const { otherProp, component, ...componentProps } = props;
  return React.createElement(component, componentProps);
};

Error:

Error:(11, 41) TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'Pick<Props<C> & C, Exclude<keyof C, "component" | "otherProp">>' is not assignable to parameter of type 'Attributes & C'.
      Type 'Pick<Props<C> & C, Exclude<keyof C, "component" | "otherProp">>' is not assignable to type 'C'.
        'Pick<Props<C> & C, Exclude<keyof C, "component" | "otherProp">>' is assignable to the constraint of type 'C', but 'C' could be instantiated with a different subtype of constraint '{}'.
Shivam Singla

Here we need to understand some utility types and how the destructuring happens in TS.

type Obj = {
  [key: string]: any
}

interface I1 {
  a: number
  b: number
  c: number
}

const i1: I1 = {
  a: 1,
  b: 1,
  c: 1,
}

let {a, ...rest} = i1

interface Obj {
    [key: string]: any
}

const i2: Obj & I1 = {
  a: 1,
  b: 1,
  c: 1,
  d: 1,
  e: 1,
}

let {a: a1, b, c, ...rest2} = i2

function func<T extends Obj>(param: I1 & T) {
    const {a, b, c, ...rest} = param
}

In the above code, the inferred type for rest will be {b: number, c: number} because object i1 contains only three keys and one of them aka a is exhausted. In the case of rest2, TS can still infer type to Obj as keys from interface I1 are exhausted. By exhausted I mean they are not captured using rest operator.

But in case of function, TS is not able to do this type of inference. I don't know the reason why TS is not able to do. That may be due to limitation generics.

What happens in case of a function is that the type for rest inside the function is Pick<I1 & T, Exclude<keyof T, "a" | "b" | "c">>. Exclude excludes keys a, b and c from the generic type T. Check Exclude here. Then, Pick creates a new type from I1 & T with keys returned by Exclude. Since T can be any type, TS is not able to determine the keys after exclusion and hence the picked keys and hence the newly created type even though T is constrained to Obj. That's why the type variable rest in the function remains Pick<I1 & T, Exclude<keyof T, "a" | "b" | "c">>.

Please note that type returned by Pick is a subtype of Obj

Now coming to the question, the same situation happens with componentProps. The type inferred will be Pick<Props<C> & C, Exclude<keyof C, "otherProp" | "component">>. TS will not be able to narrow it down. Looking at the signature of React.createElement

 function createElement<P extends {}>(
        type: ComponentType<P> | string,
        props?: Attributes & P | null,
        ...children: ReactNode[]): ReactElement<P>

And calling it

React.createElement(component, componentProps)

The inferred type for P in the signature will be C in your code from the first argument i.e. component because it has type React.ComponentType<C>. The second argument should be either undefined or null or C (ignoring Attributes as of now). But the type of componentProps is Pick<Props<C> & C, Exclude<keyof C, "otherProp" | "component">>, which is definitely assignable to {} but not to C because it is subtype of {} not of C. C is also a subtype of {} but the pick type and C may or may not be compatible (this is same as - there is a class A; B and C derives A, objects of B and C are assignable to A, but object of B is not ascribable to C). That's why the error

        'Pick<Props<C> & C, Exclude<keyof C, "component" | "otherProp">>' is assignable to the constraint of type 'C', but 'C' could be instantiated with a different subtype of constraint '{}'.

As we are more intelligent than TS compiler, we know that they are compatible but TS does not. So make TS believe that we are doing correct, we can do a type assertion like this

type Props<C> = {
  component: React.ComponentType<C>;
  otherProp: string;
};

const MyComponent = <C extends {}>(props: Props<C> & C) => {
  const { otherProp, component, ...componentProps } = props;
  return React.createElement(component, componentProps as unknown as C);
  // ------------------------------------------------^^^^^^^^
};

This is definitely a correct type assertion because we know that type of componentProps will be C

Hope this answers your question and solves your problem.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

TypeScript is not enforcing generic intersection type (React component props)

From Dev

Extend React Abstract Component in Typescript

From Dev

How to extend a React component?

From Dev

Generic props in react/typescript - how to get T to be the type of a property?

From Dev

Generic Extended React Component Typescript

From Dev

TypeScript error in passing props to child component in react

From Java

React - How to wait for component props

From Dev

How to inject into React Component props?

From Dev

How to export React component with props?

From Java

How to set component default props on React component

From Dev

Typescript how to pass props in parent component

From Java

How to Pass Props to Modal Component in React Native

From Java

How to use generics in props in React in a functional component?

From Dev

How to test a React component with different props with Enzyme

From Dev

React.render in TypeScript for component with both props and state

From Dev

How to allow certain combinations of props in React Typescript?

From Dev

How to allow certain combinations of props in React Typescript?

From Dev

How to extend React component class created via React.createClass

From Dev

How to pass a component as props to another component in react js?

From Dev

React not updating component props?

From Dev

TypeScript & React/JSX: tsc compiler error when supplying props to react component's superclass

From Dev

How to extend a class in TypeScript?

From Java

How to pass an object as props to a child Functional Component in React.js?

From Dev

How to pass a function through props in React to a function in a different component?

From Dev

How do i pass a formated React Component Props?

From Dev

How to optimize small updates to props of nested component in React + Redux?

From Dev

How to deal with component reference variables and props in React 0.12+?

From Dev

How to pass and execute functions as props in class Component in React Native?

From Dev

How to dynamically build an object for a React Component using props?

Related Related

  1. 1

    TypeScript is not enforcing generic intersection type (React component props)

  2. 2

    Extend React Abstract Component in Typescript

  3. 3

    How to extend a React component?

  4. 4

    Generic props in react/typescript - how to get T to be the type of a property?

  5. 5

    Generic Extended React Component Typescript

  6. 6

    TypeScript error in passing props to child component in react

  7. 7

    React - How to wait for component props

  8. 8

    How to inject into React Component props?

  9. 9

    How to export React component with props?

  10. 10

    How to set component default props on React component

  11. 11

    Typescript how to pass props in parent component

  12. 12

    How to Pass Props to Modal Component in React Native

  13. 13

    How to use generics in props in React in a functional component?

  14. 14

    How to test a React component with different props with Enzyme

  15. 15

    React.render in TypeScript for component with both props and state

  16. 16

    How to allow certain combinations of props in React Typescript?

  17. 17

    How to allow certain combinations of props in React Typescript?

  18. 18

    How to extend React component class created via React.createClass

  19. 19

    How to pass a component as props to another component in react js?

  20. 20

    React not updating component props?

  21. 21

    TypeScript & React/JSX: tsc compiler error when supplying props to react component's superclass

  22. 22

    How to extend a class in TypeScript?

  23. 23

    How to pass an object as props to a child Functional Component in React.js?

  24. 24

    How to pass a function through props in React to a function in a different component?

  25. 25

    How do i pass a formated React Component Props?

  26. 26

    How to optimize small updates to props of nested component in React + Redux?

  27. 27

    How to deal with component reference variables and props in React 0.12+?

  28. 28

    How to pass and execute functions as props in class Component in React Native?

  29. 29

    How to dynamically build an object for a React Component using props?

HotTag

Archive