I have three different interfaces which share the same property and want to create a custom typeguard that will check which of interfaces value
from [ value, setValue ] = useState({})
has, and depending on it, run adequate setValue(...value, property: newValue);
To better illustrate the situation I copied code written by captain-yossarian from Either type containing different types question, changed it as suggested in the replies, added a third interface that contains type
properties, same as the other two interfaces and added typeguards.
import React from 'react';
interface Animal {
type: string;
age: number;
}
interface Cat extends Animal {
typeOfCat: string;
}
interface Bus {
type: string;
model: string;
}
type AnimalOrCatOrBus<T> = T extends any ? {
value: T;
setValue: React.Dispatch<React.SetStateAction<T>>;
} : never;
type Props = AnimalOrCatOrBus<Animal | Cat | Bus>;
const ChangeType = (props: Props): JSX.Element => {
type MergeTypes = Animal | Cat | Bus;
function isCat(v: MergeTypes): v is Cat {
return (v as Cat).typeOfCat !== undefined;
};
function isAnimal(v: MergeTypes): v is Animal {
return !isCat(v) && (v as Animal).type !== undefined;
};
function isBus(v: MergeTypes): v is Bus {
return (v as Bus).model !== undefined;
};
const { value, setValue } = props;
const handleChange = (data: string) => {
if (isAnimal(value)) {
setValue({ ...value, type: data });
} else if (isCat(value)) {
setValue({ ...value, type: data });
} else if (isBus(value)) {
setValue({ ...value, type: data });
}
};
return (
<form>
<label>
<input
type="text"
value={value.type}
onChange={(data) => handleChange(data.target.value)}
/>
</label>
</form>
);
};
const jsx = <ChangeType value={{ type: 'animal', age: 42 }} setValue={() => { }}/>;
const jsx2 = <ChangeType value={{ type: 'animal', age: 42, typeOfCat: 'cat' }} setValue={() => { }}/>;
const jsx3 = <ChangeType value={{ type: 'vehicle', model: 'volksvagen' }} setValue={() => { }}/>;
From what I see the typeguards works for value
and compiler see that value
is Bus
, Cat
or Animal
after putting it through if(isX(value))
function which use type predicates, but setValue
stays React.Dispatch<React.SetStateAction<Bus>> & React.Dispatch<React.SetStateAction<Cat>> & React.Dispatch<React.SetStateAction<Animal>>
and I don't know how to create such typeguard for setValue
for it to know which of T = Animal | Bus | Cat
in React.Dispatch<React.SetStateAction<T>>
it should expect.