Skip to content
Advertisement

Typescript dynamically infer type from object

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.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement