简体   繁体   中英

Typescript dynamically assign generic type based on a variable

I want to use a type (or interface) to extends a React component.

Definition:

type CoreProps<T> = {
  htmlElement?: 'article' | 'aside' // ...
} & React.HTMLAttributes<T>

Usage desired:

type MyComponentProps = {
  myComponentFirstProp: string
  // ...
}

const MyComponent = ({ 
  htmlElement, 
  myComponentFirstProp,
  ...props
}: MyComponentProps & CoreProps): JSX.Element => {
  // ...
}

I know I can replace <T> by HTMLElement but is there a way to type CoreProps according to the input htmlElement passed in MyComponent ?

There is JSX.IntrinsicElements type in which you can obtain relevant HTMLAttributes , so you can do it in this way.

interface CoreProps<T> {
  htmlElement: T;
}

interface MyComponentProps {
  myComponentFirstProp?: string;
}

const MyComponent = <T extends keyof JSX.IntrinsicElements>(
  props: CoreProps<T> & JSX.IntrinsicElements[T] & MyComponentProps
): JSX.Element => {
  ...
};

<MyComponent htmlElement="img" src="." />; // OK
<MyComponent htmlElement="a" href="." />; // OK
<MyComponent htmlElement="article" />; // OK
<MyComponent htmlElement="a" src="." />; // Error, no 'src' attribute in <a> element

If you want the corresponding type of attributes

import { DetailedHTMLFactory, ReactHTML } from 'react';

type EleAttributes<
  T extends keyof ReactHTML
> = ReactHTML[T] extends DetailedHTMLFactory<infer R, infer P> ? R : never;

EleAttributes<'input'>
EleAttributes<'aside'>
...

Update

Based your example:

type CoreProps<T extends keyof JSX.IntrinsicElements> = {
  htmlElement?: T;
} & JSX.IntrinsicElements[T];

type ComponentProps = {
  display?: string;
};

const Component = <T extends keyof JSX.IntrinsicElements>({
  htmlElement,
  display,
  style,
  children,
  ...props
}: ComponentProps & CoreProps<T>) => {
  const styles = {
    ...style,

    display,
    // more ...
  };

  // Set default 'div' when htmlElement is undefined
  return React.createElement(
    htmlElement || 'div',
    {
      style: styles,
      ...props,
    },
    children
  );
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM