I need to figure out the TypeScript type of data coming into my React component. The data could represent one of two things, cats or dogs:
my-component.tsx:
export const MyComponent = { props: {
data: any; // I wish I could use the union type "Cat[] | Dog[]" here
}}: JSX.Element => {
...
return (
<ComponentNotModifiableByMe
data={props.data}
// If "Cat[] | Dog[]" is used above, TS says "Type 'Dog[]' is not assignable to type 'Cat[]'. ts(2322)"
// If "any" is used above, this works okay, but defeats the purpose of TypeScript
...
/>
);
}
cat-stuff.tsx:
export type Cat = {
foo: string;
bar: string;
address: string;
birthday: string;
}
dog-stuff.tsx:
export type Dog = {
bar: string;
address: string;
birthday: string;
}
There appear to be five ways to check what things are in TypeScript, based on an existing SO answer:
instanceof
N/A, this checks against classes, but I have types
typeof
N/A, this checks against primitive types, but I have custom TypeScript types
in
This would probably work if I had Cat | Dog. But I have arrays, and it's valid in my app for data to be an empty array. So, I can't rely on checking the first element of data, because it might not exist.
user-defined type guard, AKA type predicates
This seems the most promising, but as with #3, there might not be any actual data for me to check, so I don't know what I would put in an isCat() or isDog() function.
discriminated union
N/A, there is no kind property or equivalent on my types that is a unique identifier; adding one could be a workaround, but it might not work anyways, for the same reason as #3 and #4 above.
How can I figure out the type of data in a way that works for empty arrays? Currently I'm manually passing an extra prop to MyComponent but that doesn't feel like good coding practice. If there's a React way around this problem (instead of a TypeScript way), that'd be fine too.
If the array is empty there is no possible way to infer its type, so your extra prop is required.
You could write something like this:
export const MyComponent = { props: {
data: Cat[] | Dog[],
cats: boolean,
dogs: boolean,
}}: JSX.Element => {
// After calling this function in a if, data will be typed as Cat[]
isArrayOfCats(data: Cat[] | Dog[]): data is Cat[] {
return cats;
}
// After calling this function in a if, data will be typed as Dog[]
isArrayOfDogs(data: Cat[] | Dog[]): data is Dog[] {
return dogs;
}
return (
<ComponentNotModifiableByMe
data={props.data}
// If "Cat[] | Dog[]" is used above, TS says "Type 'Dog[]' is not assignable to type 'Cat[]'. ts(2322)"
// If "any" is used above, this works okay, but defeats the purpose of TypeScript
...
/>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With