Typescript: Regression: IndexedAccess T[keyof T] is not assignable to anything

Created on 23 Jan 2018  路  9Comments  路  Source: microsoft/TypeScript



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

Bug Fixed

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.

All 9 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Antony-Jones picture Antony-Jones  路  3Comments

weswigham picture weswigham  路  3Comments

zhuravlikjb picture zhuravlikjb  路  3Comments

bgrieder picture bgrieder  路  3Comments

dlaberge picture dlaberge  路  3Comments