Skip to content

Strange keyof behavior with combination of Partial and Record. #28839

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
fenying opened this issue Dec 4, 2018 · 5 comments
Closed

Strange keyof behavior with combination of Partial and Record. #28839

fenying opened this issue Dec 4, 2018 · 5 comments
Assignees
Labels
Bug A bug in TypeScript Domain: Indexed Access Types The issue relates to accessing subtypes via index access Domain: Mapped Types The issue relates to mapped types Fixed A PR has been merged for this issue

Comments

@fenying
Copy link

fenying commented Dec 4, 2018

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:

#18538 maybe

@weswigham weswigham added Bug A bug in TypeScript Domain: Indexed Access Types The issue relates to accessing subtypes via index access Domain: Mapped Types The issue relates to mapped types labels Dec 4, 2018
@DanielRosenwasser
Copy link
Member

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

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Dec 8, 2018

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

@fenying
Copy link
Author

fenying commented Dec 8, 2018

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

@ahejlsberg
Copy link
Member

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.

@bowenni
Copy link

bowenni commented Jan 30, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: Indexed Access Types The issue relates to accessing subtypes via index access Domain: Mapped Types The issue relates to mapped types Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

5 participants