Typescript: Strange keyof behavior with combination of Partial and Record.

Created on 4 Dec 2018  路  5Comments  路  Source: microsoft/TypeScript


TypeScript Version: 3.2.1


Search Terms:
keyof, Record, Partial, ...

But actually, I don't know how to exactly express this problem in the search box...

Code

// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.

interface ITest {

    a: string;
}

type PartialRecord<K extends keyof any, T> = {

    [k in K]?: T;
};

class SlotTest<ES> {

    private _slots1: Partial<Record<keyof ES, ITest>> = {};

    private _slots2: Record<keyof ES, ITest> = {} as any;

    private _slots3: PartialRecord<keyof ES, ITest> = {};

    public doTest1<K extends keyof ES>(name: K): void {

        /**
         * It works well in v2.9.x, but fails in v3.2.1.
         * [ts] Type '{ a: string; }' is not assignable to type 'Record<keyof ES, ITest>[K]'. [2322]
         */
        this._slots1[name] = {"a": "ggg"};
    }

    public doTest2<K extends keyof ES>(name: K): void {

        /**
         * This works fine.
         */
        this._slots2[name] = {"a": "ggg"};
    }

    public doTest3<K extends keyof ES>(name: K): void {

        /**
         * This works fine.
         */
        this._slots3[name] = {"a": "ggg"};
    }
}

Expected behavior:

When I used v2.9.2, the code works well.

Actual behavior:

But, when I upgrade to v3.2.1, it fails with compilation error message like this:

Type '{ "a": string; }' is not assignable to type 'Record<keyof ES, ITest>[K]'.

Playground Link:

Related Issues:

https://github.com/Microsoft/TypeScript/issues/18538 maybe

Bug Indexed Access Types Mapped Types Fixed

Most helpful comment

A simplified repro:

function foo<T, K extends keyof T>() {
    let x: Partial<Record<keyof T, string>>[K] = "hello";  // Error, but should be ok
}

So, the reason it worked with 2.9.x is that we had some serious holes in checking of assignments to indexed access types--we'd permit assignments of pretty much anything. We are now being more thorough, which is uncovering other design limitations. In this particular case, the limitation is that we do not simplify indexed accesses applied to mapped types beyond one level because it could cause infinite recursion when mapped types circularly reference themselves. However, I think we have a fairly compelling example here of why we ought to simplify at least a few levels deep.

All 5 comments

This seems reasonable; assigning any concrete value to a generic is almost certainly incorrect. @weswigham any reason you believe this is a bug?

Oh, I see, the property types aren't generic.

@DanielRosenwasser Yes. What makes me confused is that PartialRecord<K, V> is okay but Partial<Record<K, V>> does not.

A simplified repro:

function foo<T, K extends keyof T>() {
    let x: Partial<Record<keyof T, string>>[K] = "hello";  // Error, but should be ok
}

So, the reason it worked with 2.9.x is that we had some serious holes in checking of assignments to indexed access types--we'd permit assignments of pretty much anything. We are now being more thorough, which is uncovering other design limitations. In this particular case, the limitation is that we do not simplify indexed accesses applied to mapped types beyond one level because it could cause infinite recursion when mapped types circularly reference themselves. However, I think we have a fairly compelling example here of why we ought to simplify at least a few levels deep.

Can you please reopen this issue?

I have the following two level mapped types

function f<K extends string, V, T extends Partial<Record<K, V[]>>>(
    object: T,
    key: K,
    value: V[],
) {
  object[key] = value;
}

Expected behavior:
No errors.

Actual behavior:
index.ts:6:3 - error TS2322: Type 'V[]' is not assignable to type 'T[K]'.

TypeScript Version: 3.4.0-dev.20190130

I think my repro is a duplicate of this issue, but happy to file a separate bug if you find these two different.

Was this page helpful?
0 / 5 - 0 ratings