Description
TypeScript Version: 2.3.2
Code
function ff<T extends {[k: string]: number | boolean}, K extends keyof T>(x: T, k: K, v: T[K]): void {
}
let val = {a: 5}
ff(val, 'a', 9)
// PROBLEM: argument of type '9' is not assignable to parameter of type '5'
ff({a: 5}, 'a', 9)
function gg<T extends {[k: string]: number | string}, K extends keyof T>(x: T, k: K, v: T[K]): void {
}
gg({a: 5}, 'a', 9)
gg(val, 'a', 9)
Expected behavior:
The type inferencer makes no difference between any of those four calls.
Actual behavior:
The type inferencer seems to treat the generic T differently based on whether there's a literal in the union of allowed types and based on whether the argument is passed from a variable or directly. The first ff()
call infers a
as the literal 5 while the second one infers it as number
.
This happens when you have a literal in the union. I suppose boolean
is equivalent to true | false
? You can trigger the same behavior when replacing boolean
with some string literal. However, if you use number | string
like in gg
the type of a
becomes number
in both cases.
What's unexpected here is that I've told the compiler about my allowed types: number | boolean
. No other type (not even a more specific one) should be inferred unless I explicitly list it (e.g.: number | 5
). Especially, some unrelated literal (boolean
) shouldn't change the inference behavior of number
.