TypeScript doesn't match conditional types properly

Viewed 91

I have a following component (simplified version):

type ValueHelper<Clearable extends boolean> = Clearable extends true
    ? string | null
    : string;

type Props<Clearable extends boolean> = {
    clearable?: Clearable;
    onChange: (value: ValueHelper<Clearable>) => void;
    value: ValueHelper<Clearable>;
};

const Input = <Clearable extends boolean = false>({ clearable, value, onChange }: Props<Clearable>) => {
    const handleChange = (event) => {
        const inputValue = event.target.value;
        if (clearable && inputValue === '') {
            onChange(null);
            return;
        }
        onChange(inputValue);
    }

    return (
        <input value={value} onChange={handleChange} />
    );
}

For some reason TypeScript complains about null argument in onChange function, which seems to be wrong, since my conditional type ValueHelper checks if clearable is true, and if so, null should be allowed.

enter image description here

Any ideas?

1 Answers

This is a limitation of Typescript; Typescript does control-flow type narrowing on variables, but not on type variables like your Clearable. So a test like if(clearable) can narrow the type of the variable clearable to be something more specific than Clearable, but it cannot narrow Clearable itself to be something more specific. Therefore as far as Typescript is concerned, it's as if you didn't do the if check and Clearable could still be string instead of string | null.

In this case a type assertion is probably the simplest solution: you know that null is assignable to ValueHelper<Clearable> in this branch even if Typescript doesn't know it, so instead of null you can write null as ValueHelper<Clearable>.

Related