I have a JS Object with React components, indexed by ID.
const MODAL_ENTITIES = { changeEmail: ChangeEmailModal, changeUsername: ChangeUsernameModal, };
I would like to have a ModalEntity
type which results in this:
type ModalEntity = { id: 'changeEmail', props: React.ComponentProps<typeof ChangeEmailModal> } | { id: 'changeUsername', props: React.ComponentProps<typeof ChangeUsernameModal> };
My problem is, I want the type to be dynamically generated from the MODAL_ENTITIES
object, since I want the process of adding a modal to be as effortlessly as possible.
Is there a way to define this type dynamically? I could do this but I want to avoid generics, I would like T
to be inferred:
export type ModalEntity<T extends keyof typeof MODAL_ENTITIES> = { id: T; props: React.ComponentProps<typeof MODAL_ENTITIES[T]>; };
Advertisement
Answer
I made a mockup. The idea is to get generic T
out of your ModalEntity
type so that it can be used easily when you add a new modal.
Placeholders for your modals, assuming that each modal has different props:
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 };
Then we get the keys from your MODAL_ENTITIES
in a dynamic way:
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>;
Finally:
export type ModalEntity = { [K in ModalEntitiesKeys]: { id: K; props: React.ComponentProps<typeof MODAL_ENTITIES[K]>; }; }[ModalEntitiesKeys];
The ModalEntity
type will look like this and it’s no longer generic. the type of props
fields will be inferred dynamically as you requested regardless of different modal props.
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; }; }
You can elaborate more on this idea.