TypeScript Version: 2.7.0-dev.20180123
Search Terms: indexed access, type parameter keyof
Code
interface I {
foo: string;
}
declare function take<T>(p: T): void;
function fn<T extends I, K extends keyof T>(o: T, k: K) {
take<string>(o[k]); // Argument of type 'T[K]' is not assignable to parameter of type 'string'.
take<{} | null | undefined>(o[k]); // Type 'T[K]' is not assignable to type '{} | null | undefined'. Type 'T[K]' is not assignable to type '{}'.
take<any>(o[k]); // this one works
}
Expected behavior:
Compiles without error. 2.7.0-rc allows all of the above, although the type of the indexed access is not correctly inferred, see https://github.com/Microsoft/TypeScript/issues/12991#issuecomment-359834231
Actual behavior:
Error on the first two calls, see inline comments.
Playground Link:
Related Issues:
https://github.com/Microsoft/TypeScript/issues/12991
It looks like the first call to take shouldn't work, since there's no guarantee T[K] is a string. T could be an object that extends I and adds non-string properties.
But the second one should work. Previously, anything other than null and undefined could be assigned to the type '{}', but that doesn't seem to be the case anymore. Is this a bug, or by design?
Consider calling your function with:
interface A extends I {
bar: number;
}
declare var a: A;
fn(a, "bar");
So, just to confirm, the error in the following code is intended?
type unknown = {} | null | undefined;
function fn<T>(o: T, k: keyof T) {
var v: unknown = o[k]; //Type 'T[keyof T]' is not assignable to type 'unknown'.
}
Is there something T[keyof T] could be that isn't assignable to {} | null | undefined ?
that is a special case really. the general case is that you can not make assumptions about o[k]. we could add a special handling to allow it to be assignable to that type.
Before 2.7 that assignment worked, although maybe it shouldn't have. I have found a workaround using mapped types, however:
type unknown = {} | null | undefined;
function fn<K extends string, T extends {[I in K]: unknown}>(o: T, k: K) {
var v: unknown = o[k]; //Works!
}
Before 2.7 that assignment worked, although maybe it shouldn't have. I have found a workaround using mapped types
Please see my response in https://github.com/Microsoft/TypeScript/issues/21369#issuecomment-362363073
Got it - thanks for clearing that up! Not sure if special-casing for {} | null | undefined makes sense or not (especially since there are reasonable alternatives).
We talked about the unknown type one more time today. @RyanCavanaugh is working on a proposal here. it is going to be a new primitive type.
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
Most helpful comment
We talked about the unknown type one more time today. @RyanCavanaugh is working on a proposal here. it is going to be a new primitive type.