Skip to content

Assignability check of index signature incorrectly reversed with non-trivial generics #35322

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
UselessPickles opened this issue Nov 25, 2019 · 8 comments
Labels
Duplicate An existing issue was already created

Comments

@UselessPickles
Copy link

UselessPickles commented Nov 25, 2019

TypeScript Version: 3.5.1 - 3.8.0-dev.20191123

Search Terms: index signature assignable reversed

Code

type Foo<T extends Record<keyof T, number | string>> = {
  readonly [n: number]: T[keyof T];
};

declare let numberFoo: Foo<Record<string, number>>;
declare let numberLiteralFoo: Foo<Record<string, 1 | 2 | 3>>;

// UNEXPECTED ERROR HERE!
numberFoo = numberLiteralFoo;

Expected behavior:
Foo<Record<string, 1 | 2 | 3>> should be assignable to Foo<Record<string, number>> because 1 | 2 | 3 is assignable to number.

Actual behavior:
I get the following error. Notice how the direction of assignability in the error message seems to get incorrectly swapped starting on the second line of the error:

Type 'Foo<Record<string, 1 | 2 | 3>>' is not assignable to type 'Foo<Record<string, number>>'.
Type 'Record<string, number>' is not assignable to type 'Record<string, 1 | 2 | 3>'.
Index signatures are incompatible.
Type 'number' is not assignable to type '1 | 2 | 3'.(2322)

This error seems to have been introduced in version 3.5.1 (based on switching back to earlier versions of TS in the playground until there error disappeared).

Playground Link: http://www.typescriptlang.org/play/#code/C4TwDgpgBAYg9nAPAFShAHsCA7AJgZygCUIBjOAJ10QGsIQ4AzKZAGimwFcBbAIwgpQAPlHzAKAS2wBzAHyyoAXigBvAFBQoFCAENccbABsQUANrYAXBx78KAXSvJTdBs2R2A3GoC+XtbjJDHW0oQwhgaz4BeDgrGMQScipEMUkZdi4oinkvANIgkLCIzNsAGQksCh1DGLiEBLJKalSpaXYARmEoACYugGYctTUAemGoAFUAOQBRAA0ABWmAYWRpgBEoaaIiAHkiKAAJLemAQjUS6IQlSLKKgWqYvyG1UEgoACFglDRMHAIbgRdFoyBTKdSabR6AzGMyWAH2RxeXxDPIFaBFeGfChWLGIC7ZXKBYLo8Lw8qVapYnFfToiXoiAZ+UZQADKEm4YDCggwOg5YSgAAsdIRsHA0BQKJRBQIIOcbAIsdd8eT7oYsU8gA

Related Issues:

@UselessPickles
Copy link
Author

Interesting observation: I get the expected error if I reverse the assignment in my example to numberLiteralFoo = numberFoo;

Type 'Foo<Record<string, number>>' is not assignable to type 'Foo<Record<string, 1 | 2 | 3>>'.
Index signatures are incompatible.
Type 'number' is not assignable to type '1 | 2 | 3'.(2322)

Notice that this error only has 3 lines. It's missing the equivalent of the second line from the incorrect error in my original example.

@jack-williams
Copy link
Collaborator

Looks to be a duplicate of #32311

@UselessPickles
Copy link
Author

@jack-williams Is there any recommendation for working around this limitation? Any hope for a fix in the future?

@jack-williams
Copy link
Collaborator

jack-williams commented Nov 25, 2019

Does this work?

type Foo<T extends Record<keyof T, V>, V extends number | string = T[keyof T]> = {
  readonly [n: number]: V;
};

EDIT: The following is shorter:

type Foo<T extends Record<keyof T, number | string>> = Record<number, T[keyof T]>;

@UselessPickles
Copy link
Author

That looks promising. I'll give it a try tonight and report back.

@UselessPickles
Copy link
Author

UselessPickles commented Nov 26, 2019

@jack-williams Partial success with your workaround. In my oversimplified example, it seems to work across many TS versions (tested via the playground). But in my actual use case with much more complexity, I still get the same unexpected error in TS versions prior to 3.7. I'll continue to try to narrow down what's causing the error now.

I'm developing a library published on NPM, so I'm trying to support as many versions back as possible. The good news is that the workaround doesn't introduce any new problems in earlier versions. Worst case scenario, my library will work as expected in TS 3.7+, but will require some brute force typecasting in earlier versions of TS if these kinds of assignments are used (luckily not a common use case).

Thanks!

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jan 7, 2020
@RyanCavanaugh
Copy link
Member

We're thinking about this quite a bit and may special-case T[keyof T] to measure covariantly

@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants