2

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]>;
};

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer! I forgot to mention this, but not all modals have the same props. In your solution, an instance with id changeEmail would still accept the props of ChangeUsernameModal.
Hi @Robrecht, I simplified and updated my answer to be correct, taking into account the information you gave me. I feel like this is what you wanted.
Yes, that's exactly it. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.