I have a <Selector />
component which can accept an optional param isMulti: boolean
.
It also have a required param onSelected, who's signature should be changed based on isMulti value (wheater is true or not - undefined)
I've created an example here
My typescript implementation fails somewhere in the following implementation (I think):
interface ISelectorProps<T> {
isMulti?: T;
onSelected: T extends true
? ((options: string[]) => void)
: ((option: string) => void);
}
So the idea is that, if isMulti is provided, onSelected should return an array of strings, otherwise, it should provide only a string;
As you can see in the editor, TS complains here:
Argument of type 'string[]' is not assignable to parameter of type 'string & string[]'.
Type 'string[]' is not assignable to type 'string'.
The signature of onSelected is wrongly typed. Any ideas?
The simple thing to do is to define your type as either/or with the isMulti
being the selector. This is called a discriminated union
type ISelectorProps = {
isMulti: true;
onSelected: (options: string[]) => void;
} | {
isMulti: false;
onSelected: (option: string) => void;
}
function x(props: ISelectorProps) {
if (props.isMulti === true) {
props.onSelected(['hey', 'there']);
} else {
props.onSelected("only one");
}
}
So here, when isMulti
is true, you have onSelected
taking an array, and a single string otherwise.
TypeScript Playground
The TypeScript Playground should be correct, but I am finding that at least in a different environment, the else
is not type narrowing correctly. I have found that changing the check to be exact, if (props.isMulti === true)
then the compiler does resolve the else
condition to narrow to onSelected(option: string)
correctly. Not a perfect soliton, but could perhaps be done instead with an enum instead of boolean.
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