I want to create a React functional component which takes one of the predefined components as a prop and which logic depends on what component was passed in.
So these are the possible names:
enum ComponentName {
ComponentA: "component.a",
ComponentB: "component.b",
ComponentC: "component.c",
// etc
}
Now the hard part, I have a bunch of Component types, which names are all in the enum, like:
type ComponentA = {
attributes: {
// ...
}
}
type ComponentB = {
attributes: {
// ...
}
}
Now what I want to do is somehow to define type Component, so that:
type Component =
| {
id: string;
name: ComponentName.ComponentA;
component: ComponentA;
}
| {
id: string;
name: ComponentName.ComponentB;
component: ComponentB;
}
| // ...
Is there a way to simplify this union type by mapping over ComponentName
enum and matching enum prop to Component type name?
I think if you have some sort of mappings between component names and their attributes ComponentMappings
, then you can create a union from that mapping using a helper function like
type Values<T> = T[keyof T];
Values
generates a union type from the values of a mapped type or object.
enum ComponentName {
ComponentA = "component.a",
ComponentB = "component.b",
ComponentC = "component.c",
}
type ComponentMappings = {
[ComponentName.ComponentA]: {
attributes: {
className: string;
};
};
[ComponentName.ComponentB]: {
attributes: {
className: string;
foo: number;
};
};
[ComponentName.ComponentC]: {
attributes: {
className: string;
};
};
};
type Values<T> = T[keyof T];
type Component = Values<{
[K in ComponentName]: {
id: string;
name: K;
component: ComponentMappings[K];
};
}>;
const comopnentA: Component = {
name: ComponentName.ComponentA,
id: "component-a",
component: {
attributes: {
className: "flex",
},
},
};
const componentB: Component = {
name: ComponentName.ComponentB,
id: "component-B",
component: {
attributes: {
className: "flex",
foo: 3,
},
},
};
Note that Prettify
type in the playground does nothing and just makes it more readable when you hover on the type.