React use different interface in dependent on property value
I have 3 different types of items, which i have interfaces of
interface TypeA{
name: string;
}
interface TypeB{
count: number;
}
interface TypeC{
date: Date;
}
This items should be rendered in a list of items (in a list all items are from the same type). Depending on the type of the item, a different method is called, which will render different layouts.
export const ListItem: React.FC<ListItemProps> = (props) => {
let item = null;
switch (props.type) {
case "A":
item = renderTypeA(props);
break;
case "B":
item = renderTypeB(props);
break;
case "C":
item = renderTypeC(props);
break;
}
return item;
};
The method should accept just items from the desired type.
const renderTypeA = (props: TypeAProps) => {
{...}
};
The problem is that I can't get Typescript to recognize all properties of the types and also only auto-complete the respective types.
I have also tried it with a union type "ListItemTypes",
type ListItemTypes = TypeA | TypeB | TypeC
export const ListItem: React.FC<ListItemTypes> = (props) => {
...
};
but when I then try to include the ListItem, I always get an error that properties are missing.
<ListItem {...item /> <--- is telling me that properties are missing
Does anyone know how I can fix this problem?
Example
Solution 1:
I checked your code, it's throwing errors because you're destructuring an array into your component. In your code
const renderItem = () => {
const sampleData = [
{
type: "A",
count: 12,
},
];
// incorrectly destructuring sampleData here
return <ListItem {...sampleData} />;
};
You should be able to achieve the expected result with this instead:
const renderItemA = () => {
const sampleData = {
type: "A" as const, // notice const assertion here
name: "name"
};
return <ListItem {...sampleData} />;
};
Because you defined type
as literal type, you need const assertion so that
no literal types in that expression should be widened (e.g. no going from "hello" to string)
Playground link