I am encountering difficulties comprehending why the TypeScript type-checker is denying code that appears to be valid.
export type Fn<I, O> = (value: I) => O
type FInput<F extends Fn<any, any>> = F extends Fn<infer I, any> ? I : never
type FOutput<F extends Fn<any, any>> = F extends Fn<any, infer O> ? O : never
type FnMap = {
numToInt: Fn<number, number>
numToStr: Fn<number, string>
strToNum: Fn<string, number>
strToStr: Fn<string, string>
}
const fnMap: FnMap = {
numToInt: (x) => Number(x.toFixed(0)),
numToStr: (x) => x.toString(10),
strToNum: (x) => parseInt(x),
strToStr: (x) => x.toUpperCase(),
} as const
function doWork<T extends keyof FnMap>(key: T, input: FInput<FnMap[T]>): FOutput<FnMap[T]> {
const fn = fnMap[key]
return fn(input)
// Type 'string | number' is not assignable to type 'FOutput<FnMap[T]>'.
// Type 'string' is not assignable to type 'FOutput<FnMap[T]>'.
// Argument of type 'string | number' is not assignable to parameter of type 'never'.
// Type 'string' is not assignable to type 'never'.
}
In the provided example, TypeScript correctly auto-completes the doWork
function (you can experiment with it on the playground).
Is there a particular reason for this failure? Is there another way to express this?