Using React with TypeScript, I try to dynamically render components based on a field name
// Referencing all components
const components = {
ComponentA,
ComponentB,
};
// Dynamic render the component based on fieldName
const displayComponent = (
fieldName: keyof typeof components,
content: ComponentType
): React.ReactNode => {
const FieldNameComponent = components[fieldName];
return <FieldNameComponent item={content} />;
};
My types:
type ComponentTypeA = {
title: string;
content: string;
};
type ComponentTypeB = {
title: string;
excerpt: string;
};
type ComponentType = ComponentTypeA | ComponentTypeB;
The compiler is not happy because it doesn't know which type to use (Component1 or Component2 which have different properties). I get this error
Type 'ComponentType' is not assignable to type 'ComponentTypeA & ComponentTypeB'.
Type 'ComponentTypeA' is not assignable to type 'ComponentTypeB & ComponentTypeA'.
Type 'ComponentTypeA' is missing the following properties from type 'ComponentTypeB': excerpt
So I have tried this way:
const displayComponent = (
fieldName: keyof typeof components,
content: ComponentType
) => {
if (content instanceof ComponentTypeA) {
return <ComponentA item={content} />;
} else if (content instanceof ComponentTypeB) {
return <ComponentB item={content} />;
}
};
But the error remains even with the instanceof
test.
CodePudding user response:
You could use a type guard:
function isComponentTypeA(type: unknown): type is ComponentTypeA {
return type && "title" in type && "content" in type;
}
And you would use it in the condition:
if (isComponentTypeA(content)) {
return <ComponentA item={content} />; // inferred as ComponentTypeA
} else if (isComponentTypeB(content)) { // another type guard if needed
return <ComponentB item={content} />; // inferred as ComponentTypeB
}
CodePudding user response:
I ended up to solve this error this way:
const displayComponent = (
content: ComponentType
): React.ReactNode => {
switch (content.fieldName) {
case "ComponentA":
return <ComponentA item={ content as ComponentAType}/>
case "ComponentB":
return <ComponentB item={ content as ComponentBType}/>
default:
break;
}
};
But I pretty unhappy with this, because if I have to deal with a lot to components, the switch statement will be so long... A better approach is welcome.