Estaba haciendo esta pregunta con una versión más compleja de este concepto básico
Rel: ¿Pueden los Generic JSX.Elements funcionar en Typescript?
Lo reduje a los Elementos principales:
Este es el Object A que toma parámetros de TypeA
type TypeA = { label: string value: number } const ObjA = ({ label, value }:TypeA) => { return <div> <div>Label: {label}</div> <div>Value: {value}</div> </div> } Este es Object B que toma parámetros de TypeB
type TypeB = { label: string value: string bool: boolean } const ObjB = ({ label, value, bool }:TypeB) => { return <div> <div>Label: {label}</div> {bool && <div>Value: {value}</div>} </div> }Ahora recopilo este ComponentGroup dentro de una matriz y creo un Tipo a partir de esta matriz:
const ComponentCollection = [ ObjA, ObjB ] as const type Components = typeof ComponentCollection[number]Luego creo un componente genérico:
interface GenericProps<T extends Components> { Component: T title: string } const Generic = <T extends Components,>({ Component, title, ...props }:GenericProps<T>) => { return ( <div> <label>{title}</label> <Component {...props}/> </div> ) }Por fin puedo llamar al componente genérico de la siguiente manera:
<Generic Component={ObjA} title={'Usage A'} label={'Object A'} value={'String A'}/> <Generic Component={ObjB} title={'Usage B no Bool'} label={'Object B'} value={0}/> <Generic Component={ObjB} title={'Usage B with Bool'} label={'Object B'} value={0} bool/>Aunque funciona muy bien en JavaScript, estropeé algo con la escritura.
Configuré un TS-Playground y un Codepen:
TS-Patio de juegos: https://tsplay.dev/WvVarW
Codepen: https://codepen.io/Cascade8/pen/eYezGVV
/@ts-ignore<Generic Component={ObjA} ... muestra los atributos de tipo disponibles para este Objeto. En este caso: label={string: } value={string: }Siguiendo la forma en que ha construido sus tipos, puede hacerlo usando la definición de su genérico dentro de GenericProps
(Tenga en cuenta que he incluido los accesorios en un nuevo props de accesorios, ya que esto debería evitar colisiones de nombres en caso de que tenga colisiones de nombres)
import React from 'React' type TypeA = { label: string value: number } const ObjA = ({ label, value }:TypeA) => { return <div> <label>{label}</label> <label>{value}</label> </div> } type TypeB = { label: string value: string bool: boolean } const ObjB = ({ label, value, bool }:TypeB) => { return <div> <label>{label}</label> {bool && <label>{value}</label>} </div> } type Components = typeof ObjA | typeof ObjB; interface GenericProps<T extends (...args: any) => any> { Component: T title: string props: Parameters<T>[0] } const Generic = <T extends Components,>({ Component, title, props }:GenericProps<T>) => { return ( <div> <label>{title}</label> <Component {...props as any}/> </div> ) } const Usage = () => { return <Generic Component={ObjA} title={'Usage'} props={{label: 'ObjectA'}}/> } export default GenericEs posible prescindir de cualquier tipo de aserciones de tipo. Considere este ejemplo:
import React, { FC, } from 'react' type Type = { label: string; value: string | number } type TypeA = { label: string value: number } type FixSubtyping<T> = Omit<T, 'title'> const ObjA = ({ label, value }: FixSubtyping<TypeA>) => { return <div> <label>{label}</label> <label>{value}</label> </div> } type TypeB = { label: string value: string bool: boolean } const ObjB = ({ label, value, bool }: FixSubtyping<TypeB>) => { return <div> <label>{label}</label> {bool && <label>{value}</label>} </div> } type Props<T> = T & { title: string } const withTitle = <T extends Type>(Component: FC<FixSubtyping<Props<T>>>) => ({ title, ...props }: Props<T>) => ( <div> <label>{title}</label> <Component {...props} /> </div> ) const GenericA = withTitle(ObjA) const GenericB = withTitle(ObjB) const jsxA = <GenericA title={'Usage'} label={'A'} value={42} /> // // ok const jsxB = <GenericB title={'Usage'} label={'A'} value={'str'} bool={true} /> // // ok Este error ocurre porque props se infieren como Omit Omit<T,'title'> , por lo que debemos asegurarle a TS que props de componentes son compatibles con Omit<T,'title'>
Sin embargo, existe un inconveniente: debe actualizar el tipo de accesorios en otros componentes. Si no es una opción, creo que el mejor enfoque sería sobrecargar su función Generic :
import React, { FC, } from 'react' type TypeA = { label: string value: number } type FixSubtyping<T> = Omit<T, 'title'> const ObjA = ({ label, value }: TypeA) => { return <div> <label>{label}</label> <label>{value}</label> </div> } type TypeB = { label: string value: string bool: boolean } const ObjB = ({ label, value, bool }: TypeB) => { return <div> <label>{label}</label> {bool && <label>{value}</label>} </div> } type Props<T> = T & { Component: FC<T>, title: string } function Generic<T,>(props: Props<T>): JSX.Element function Generic({ Component, title, ...props }: Props<unknown>) { return ( <div> <label>{title}</label> <Component {...props} /> </div> ) } const jsxA = <Generic Component={ObjA} title={'Usage'} label={'A'} value={42} /> // // ok const jsxB = <Generic Component={ObjB} title={'Usage'} label={'A'} value={'str'} bool={true} /> // // ok