Skip to content

Conditionally Mapped Type Leads to Confusing ts(2345) Error Message #40550

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

Open
SlurpTheo opened this issue Sep 14, 2020 · 3 comments
Open

Conditionally Mapped Type Leads to Confusing ts(2345) Error Message #40550

SlurpTheo opened this issue Sep 14, 2020 · 3 comments
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@SlurpTheo
Copy link

SlurpTheo commented Sep 14, 2020

TypeScript Version:
3.9.6
4.1.0-dev.20200914

Search Terms:
2345
conditional

Code

declare type GetPropertyNamesOfType<T, RestrictToType> = {
	[K in keyof T]: T[K] extends RestrictToType ? K : never;
}[keyof T];

You can find this gnarly type here:

declare type GetPropertyNamesOfType<T, RestrictToType> = {

The following consuming/using code correctly generates ts(2345) errors for the last two lines:

interface A {
	req: string;
	num: number;
	str: string;
}
interface B {
	req: string;
	num?: number;
	str?: string;
}

declare function callA(name: GetPropertyNamesOfType<A, string>): void;
declare function callB(name: GetPropertyNamesOfType<Required<B>, string>): void;

const prop = "someKey";
callA(prop); // ts(2345)
callB(prop); // ts(2345)

Expected behavior:
In [email protected] & [email protected] (and a few earlier versions I tried) the error message for both lines is:

Argument of type '"someKey"' is not assignable to parameter of type '"req" | "str"'.

Actual behavior:
In typescript@^3.9.6 this is reported for both lines:

Argument of type '"someKey"' is not assignable to parameter of type 'GetPropertyNamesOfType<A, string>'.

That's correct (nice even!) for the callA() line, but it's confusing for the callB() line.

(This reproduction is a single file that can run into this problem... it's not how I ran into it. I had a ts.Program with many 10s of *.ts files in the same compilation and the message was referring to some type that made no sense in the source file that was giving me the error.)

Playground Link:
https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAXAngBwQcRIgCjA9ujCgHJQC2IAzgPIBmAKmiADwMA08AStYjAJZhEDfE3QA+eAF54AbwCwAKACQAbQDS8fgDt4AaxDJ8deAwC6ALlMaz8EAA9EIbcCrdeAoSLEIA-PE0rbRAANxAYAG4lAF9VAyMTcyjFJR0nGDooMAQAQTklZTgARysqPh0Ac2TlbQBXciD6gCNw6rKYUvLtKpjU7XTM7PgAIXyVYs6Bbuq68l9G8hbIgvb5+HbK5OilJVBIWAQ6Wu0hfnxdMCgICByACm0KECssXAIiUkfaRmYWHM4N7riACUVhC+H4wGSe2gcHgRxOiDOFyuEGG90ez2weEI4Q+lC+PhYPCKtX4cGALGG4n+XQqwNB4MhO0UYHOZXgqDe0ngACIqPhKOpDDzkpdrndOYQgRF4AB6WVIKi3ABMAGYACwAViBSjFqNuktQ0rlCsQSrVWp1iiAA

Related Issues:
I found reference to the gnarly type above in #32608 & #33568.
It looks to be the same "monster" as you see in #29505 too.

@SlurpTheo SlurpTheo changed the title Conditionally Mapped Type Leads to Confusing ts(2345) Error Conditionally Mapped Type Leads to Confusing ts(2345) Error Message Sep 14, 2020
@RyanCavanaugh
Copy link
Member

I'm not sure that this is better or worse. If the union had hundreds of elements, as is common in many JSX scenarios, then it'd be much worse to just see a big list of string literals without any context as to where they came from. If the type alias name is descriptive, then a human reading it should be able to understand it from a higher-order perspective.

Tagging Needs Proposal if someone wants to figure out some motivating set of use cases for each behavior and determine what the right way to figure out the "correct" display is.

@RyanCavanaugh RyanCavanaugh added Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript labels Sep 16, 2020
@SlurpTheo
Copy link
Author

SlurpTheo commented Sep 25, 2020

You know, I was picturing a response much like this from you as I was writing this issue 😺
I hear ya.
I mainly submitted to make sure this was a deliberate, desired change beginning with [email protected].

At some point union literals do stop listing all constituents with the | ... 4 more ... | portion of the below error message.

declare function function2(p?: keyof tLotsAProps2): void;
type tLotsAProps2 = {
	p10: string;
	p11: string;
	p12: string;
	p13: string;
	p14: string;
	p15: string;
	p16: string;
	p17: string;
	p18: string;
	p19: string;
	p20: string;
	p21: string;
	p22: string;
	p23: string;
	p24: string;
	p25: string;
	p26: string;
	p27: string;
	p28: string;
	p29: string;
	p30: string;
	p31: string;
	p32: string;
	p33: string;
	p34: string;
	p35: string;
	p36: string;
};
const prop2 = "someKey";
function2(prop2); // ts(2345)
Argument of type '"someKey"' is not assignable to parameter of type '"p10" | "p11" | "p12" | "p13" | "p14" | "p15" | "p16" | "p17" | "p18" | "p19" | "p20" | "p21" | "p22" | "p23" | "p24" | "p25" | "p26" | "p27" | "p28" | "p29" | "p30" | "p31" | "p32" | ... 4 more ... | undefined'.

Now, as you say, without context that might not be too helpful either... there will be no winning I guess.

I could imagine, with inspiration from the current conditional/mapped types experience, this error instead being:

Argument of type '"someKey"' is not assignable to parameter of type 'keyof tLotsAProps2 | undefined'.

@SlurpTheo
Copy link
Author

I tweaked the main text just a bit.
The behaviour change was between [email protected] & [email protected].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

2 participants