Search code examples
typescripttype-inferenceoverload-resolution

Issue with type inference in function overloading in TypeScript


Why the following example fails to compile?

function mapObjectValues<I, O, K extends keyof any>(obj: Record<K, I>, map: (value: I, key: K) => O): Record<K, O>
function mapObjectValues<K extends keyof any, I extends Record<K, any>, O extends Record<K, any>>(obj: I, map: (value: I[K], key: K) => O[K]): O
function mapObjectValues<I, O>(obj: Record<keyof any, I>, map: (value: I, key: keyof any) => O): Record<keyof any, O>
function mapObjectValues<I, O>(_obj: Record<keyof any, I>, _map: (value: I, key: keyof any) => O): Record<keyof any, O> {
    throw "not relevant"
}

function checker(_input: Record<string, string>) { }

// compiles
checker(mapObjectValues({
    foo: "bar"
}, (_, k) => k))

// compiles
checker(mapObjectValues({
    ["foo"]: "bar"
}, (_, k) => k))

// fails with:
//
// Argument of type 'Record<string | number, string | number>' is not assignable to parameter of type 'Record<string, string>'.
//   'string' index signatures are incompatible.
//     Type 'string | number' is not assignable to type 'string'.
//       Type 'number' is not assignable to type 'string'.
checker(mapObjectValues({
    [{
        foo: "foo"
    }.foo]: "bar"
}, (_, k) => k))

// compiles
checker(mapObjectValues({
    [({
        foo: "foo"
    } as const).foo]: "bar"
}, (_, k) => k))

Playground

Is it a current limitation of type inference in TypeScript?


Solution

  • This is due to keyof of indexed types returning number | string.

    type foo = keyof { [x: string]: string; }
    //   ^? type foo = number | string
    

    Per this issue on the Typescript GH, it works as intended.