I have the following (simplified) code:
type GetInput<U> = {
defaultValue?: U
}
const getBool = (input: GetInput<boolean>) => {
return input.defaultValue ?? true
}
const getNumber = (input: GetInput<number>) => {
return input.defaultValue ?? 3;
}
type GetFunc<U> = (input: GetInput<U>) => U;
type ToType<T extends 'boolean' | 'number'> =
T extends 'boolean'
? boolean
: T extends 'number'
? number
: never;
type GlobalGetInput<
T extends 'boolean' | 'number',
U extends ToType<T>
> = {
type: T;
defaultValue?: U;
};
const get = <T extends 'boolean' | 'number', U extends ToType<T>>(input: GlobalGetInput<T, U>) => {
let func: GetFunc<U>;
switch (input.type) {
case 'boolean':
func = getBool;
break;
case 'number':
func = getNumber;
break;
default:
throw new Error('Invalid type')
}
return func({ defaultValue: input.defaultValue })
}
get({ type: 'boolean', defaultValue: true })
It works correctly when run, but the typing fails in func = getBool;
with
Type '(input: GetInput<boolean>) => boolean' is not assignable to type 'GetFunc<U>'.
Types of parameters 'input' and 'input' are incompatible.
Type 'GetInput<U>' is not assignable to type 'GetInput<boolean>'.
Type 'U' is not assignable to type 'boolean'.
Type 'ToType<T>' is not assignable to type 'boolean'.
Type 'boolean | (T extends "number" ? number : never)' is not assignable to type 'boolean'.
Type 'T extends "number" ? number : never' is not assignable to type 'boolean'.
Type 'number' is not assignable to type 'boolean'.
Type 'number' is not assignable to type 'boolean'.
Type 'number | boolean' is not assignable to type 'boolean'.
Type 'number' is not assignable to type 'boolean'.
This happens because I do not know how to tell the compiler that U can only be of type boolean
if the string boolean
is passed in the type parameter. If it was an union of objects, we could discriminate using a static property but with a union of scalars I have no clue.
Thanks!