Tengo un objeto JS con componentes React, indexado por ID.
const MODAL_ENTITIES = { changeEmail: ChangeEmailModal, changeUsername: ChangeUsernameModal, }; Me gustaría tener un tipo ModalEntity que resulte en esto:
type ModalEntity = { id: 'changeEmail', props: React.ComponentProps<typeof ChangeEmailModal> } | { id: 'changeUsername', props: React.ComponentProps<typeof ChangeUsernameModal> }; Mi problema es que quiero que el tipo se genere dinámicamente a partir del objeto MODAL_ENTITIES , ya que quiero que el proceso de agregar un modal sea lo más sencillo posible.
¿Hay alguna manera de definir este tipo dinámicamente? Podría hacer esto pero quiero evitar los genéricos, me gustaría que se infiera T :
export type ModalEntity<T extends keyof typeof MODAL_ENTITIES> = { id: T; props: React.ComponentProps<typeof MODAL_ENTITIES[T]>; };Hice una maqueta. La idea es sacar la T genérica de su tipo ModalEntity para que pueda usarse fácilmente cuando agregue un nuevo modal.
Marcadores de posición para tus modales, asumiendo que cada modal tiene accesorios diferentes:
import React from 'react'; const ChangeEmailModal: React.FC<{ id: string; name: string; email: string }> = ({ id, ...props }) => { return ( <div id={id}> {props.name} {props.email} </div> ); }; const ChangeUsernameModal: React.FC<{ id: string; otherName: string; username: string }> = ({ id, ...props }) => { return ( <div id={id}> {props.otherName} {props.username} </div> ); }; const MODAL_ENTITIES = { changeEmail: ChangeEmailModal, changeUsername: ChangeUsernameModal }; Luego obtenemos las claves de sus MODAL_ENTITIES de forma dinámica:
export type ModalEntities = typeof MODAL_ENTITIES; // this gets all the keys in type ModalEntities type StringKeys<T> = { [k in keyof T]: k; }[keyof T]; type ModalEntitiesKeys = StringKeys<ModalEntities>;Finalmente:
export type ModalEntity = { [K in ModalEntitiesKeys]: { id: K; props: React.ComponentProps<typeof MODAL_ENTITIES[K]>; }; }[ModalEntitiesKeys]; El tipo ModalEntity se verá así y ya no es genérico. el tipo de campos de props se inferirá dinámicamente según lo solicite, independientemente de los diferentes accesorios modales.
type ModalEntity = { id: "changeEmail"; props: { id: string; name: string; email: string; } & { children?: React.ReactNode; }; } | { id: "changeUsername"; props: { id: string; otherName: string; username: string; } & { children?: React.ReactNode; }; }Puedes profundizar más en esta idea.