Skip to content

Commit 5ecd03e

Browse files
authored
Merge pull request #23660 from Microsoft/fixIndexTypeTargetConstraint
Fix constraint of 'keyof T[K]' in target position
2 parents d7d220d + ad4f83a commit 5ecd03e

File tree

5 files changed

+191
-41
lines changed

5 files changed

+191
-41
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10537,11 +10537,14 @@ namespace ts {
1053710537
}
1053810538
}
1053910539
// A type S is assignable to keyof T if S is assignable to keyof C, where C is the
10540-
// constraint of T.
10541-
const constraint = getConstraintForRelation((<IndexType>target).type);
10542-
if (constraint) {
10543-
if (result = isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors)) {
10544-
return result;
10540+
// simplified form of T or, if T doesn't simplify, the constraint of T.
10541+
if (relation !== definitelyAssignableRelation) {
10542+
const simplified = getSimplifiedType((<IndexType>target).type);
10543+
const constraint = simplified !== (<IndexType>target).type ? simplified : getConstraintOfType((<IndexType>target).type);
10544+
if (constraint) {
10545+
if (result = isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors)) {
10546+
return result;
10547+
}
1054510548
}
1054610549
}
1054710550
}

tests/baselines/reference/keyofAndIndexedAccess.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,15 @@ type Predicates<TaggedRecord> = {
566566
[T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T]
567567
}
568568

569+
// Repros from #23592
570+
571+
type Example<T extends { [K in keyof T]: { prop: any } }> = { [K in keyof T]: T[K]["prop"] };
572+
type Result = Example<{ a: { prop: string }; b: { prop: number } }>;
573+
574+
type Helper2<T> = { [K in keyof T]: Extract<T[K], { prop: any }> };
575+
type Example2<T> = { [K in keyof Helper2<T>]: Helper2<T>[K]["prop"] };
576+
type Result2 = Example2<{ 1: { prop: string }; 2: { prop: number } }>;
577+
569578
// Repro from #23618
570579

571580
type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }
@@ -1241,6 +1250,37 @@ declare function f3<T, K extends Extract<keyof T, string>>(t: T, k: K, tk: T[K])
12411250
declare type Predicates<TaggedRecord> = {
12421251
[T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T];
12431252
};
1253+
declare type Example<T extends {
1254+
[K in keyof T]: {
1255+
prop: any;
1256+
};
1257+
}> = {
1258+
[K in keyof T]: T[K]["prop"];
1259+
};
1260+
declare type Result = Example<{
1261+
a: {
1262+
prop: string;
1263+
};
1264+
b: {
1265+
prop: number;
1266+
};
1267+
}>;
1268+
declare type Helper2<T> = {
1269+
[K in keyof T]: Extract<T[K], {
1270+
prop: any;
1271+
}>;
1272+
};
1273+
declare type Example2<T> = {
1274+
[K in keyof Helper2<T>]: Helper2<T>[K]["prop"];
1275+
};
1276+
declare type Result2 = Example2<{
1277+
1: {
1278+
prop: string;
1279+
};
1280+
2: {
1281+
prop: number;
1282+
};
1283+
}>;
12441284
declare type DBBoolTable<K extends string> = {
12451285
[k in K]: 0 | 1;
12461286
};

tests/baselines/reference/keyofAndIndexedAccess.symbols

Lines changed: 85 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,64 +2010,113 @@ type Predicates<TaggedRecord> = {
20102010
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 564, 3))
20112011
}
20122012

2013+
// Repros from #23592
2014+
2015+
type Example<T extends { [K in keyof T]: { prop: any } }> = { [K in keyof T]: T[K]["prop"] };
2016+
>Example : Symbol(Example, Decl(keyofAndIndexedAccess.ts, 565, 1))
2017+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 569, 13))
2018+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 569, 26))
2019+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 569, 13))
2020+
>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 569, 42))
2021+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 569, 63))
2022+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 569, 13))
2023+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 569, 13))
2024+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 569, 63))
2025+
2026+
type Result = Example<{ a: { prop: string }; b: { prop: number } }>;
2027+
>Result : Symbol(Result, Decl(keyofAndIndexedAccess.ts, 569, 93))
2028+
>Example : Symbol(Example, Decl(keyofAndIndexedAccess.ts, 565, 1))
2029+
>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 570, 23))
2030+
>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 570, 28))
2031+
>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 570, 44))
2032+
>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 570, 49))
2033+
2034+
type Helper2<T> = { [K in keyof T]: Extract<T[K], { prop: any }> };
2035+
>Helper2 : Symbol(Helper2, Decl(keyofAndIndexedAccess.ts, 570, 68))
2036+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 572, 13))
2037+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 572, 21))
2038+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 572, 13))
2039+
>Extract : Symbol(Extract, Decl(lib.d.ts, --, --))
2040+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 572, 13))
2041+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 572, 21))
2042+
>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 572, 51))
2043+
2044+
type Example2<T> = { [K in keyof Helper2<T>]: Helper2<T>[K]["prop"] };
2045+
>Example2 : Symbol(Example2, Decl(keyofAndIndexedAccess.ts, 572, 67))
2046+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 573, 14))
2047+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 573, 22))
2048+
>Helper2 : Symbol(Helper2, Decl(keyofAndIndexedAccess.ts, 570, 68))
2049+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 573, 14))
2050+
>Helper2 : Symbol(Helper2, Decl(keyofAndIndexedAccess.ts, 570, 68))
2051+
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 573, 14))
2052+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 573, 22))
2053+
2054+
type Result2 = Example2<{ 1: { prop: string }; 2: { prop: number } }>;
2055+
>Result2 : Symbol(Result2, Decl(keyofAndIndexedAccess.ts, 573, 70))
2056+
>Example2 : Symbol(Example2, Decl(keyofAndIndexedAccess.ts, 572, 67))
2057+
>1 : Symbol(1, Decl(keyofAndIndexedAccess.ts, 574, 25))
2058+
>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 574, 30))
2059+
>2 : Symbol(2, Decl(keyofAndIndexedAccess.ts, 574, 46))
2060+
>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 574, 51))
2061+
20132062
// Repro from #23618
20142063

20152064
type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }
2016-
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 565, 1))
2017-
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 569, 17))
2018-
>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 569, 40))
2019-
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 569, 17))
2065+
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 574, 70))
2066+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 578, 17))
2067+
>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 578, 40))
2068+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 578, 17))
20202069

20212070
enum Flag {
2022-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 569, 56))
2071+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 578, 56))
20232072

20242073
FLAG_1 = "flag_1",
2025-
>FLAG_1 : Symbol(Flag.FLAG_1, Decl(keyofAndIndexedAccess.ts, 570, 11))
2074+
>FLAG_1 : Symbol(Flag.FLAG_1, Decl(keyofAndIndexedAccess.ts, 579, 11))
20262075

20272076
FLAG_2 = "flag_2"
2028-
>FLAG_2 : Symbol(Flag.FLAG_2, Decl(keyofAndIndexedAccess.ts, 571, 22))
2077+
>FLAG_2 : Symbol(Flag.FLAG_2, Decl(keyofAndIndexedAccess.ts, 580, 22))
20292078
}
20302079

20312080
type SimpleDBRecord<Flag extends string> = { staticField: number } & DBBoolTable<Flag>
2032-
>SimpleDBRecord : Symbol(SimpleDBRecord, Decl(keyofAndIndexedAccess.ts, 573, 1))
2033-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 575, 20))
2034-
>staticField : Symbol(staticField, Decl(keyofAndIndexedAccess.ts, 575, 44))
2035-
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 565, 1))
2036-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 575, 20))
2081+
>SimpleDBRecord : Symbol(SimpleDBRecord, Decl(keyofAndIndexedAccess.ts, 582, 1))
2082+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 584, 20))
2083+
>staticField : Symbol(staticField, Decl(keyofAndIndexedAccess.ts, 584, 44))
2084+
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 574, 70))
2085+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 584, 20))
20372086

20382087
function getFlagsFromSimpleRecord<Flag extends string>(record: SimpleDBRecord<Flag>, flags: Flag[]) {
2039-
>getFlagsFromSimpleRecord : Symbol(getFlagsFromSimpleRecord, Decl(keyofAndIndexedAccess.ts, 575, 86))
2040-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 576, 34))
2041-
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 576, 55))
2042-
>SimpleDBRecord : Symbol(SimpleDBRecord, Decl(keyofAndIndexedAccess.ts, 573, 1))
2043-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 576, 34))
2044-
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 576, 84))
2045-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 576, 34))
2088+
>getFlagsFromSimpleRecord : Symbol(getFlagsFromSimpleRecord, Decl(keyofAndIndexedAccess.ts, 584, 86))
2089+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 585, 34))
2090+
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 585, 55))
2091+
>SimpleDBRecord : Symbol(SimpleDBRecord, Decl(keyofAndIndexedAccess.ts, 582, 1))
2092+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 585, 34))
2093+
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 585, 84))
2094+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 585, 34))
20462095

20472096
return record[flags[0]];
2048-
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 576, 55))
2049-
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 576, 84))
2097+
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 585, 55))
2098+
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 585, 84))
20502099
}
20512100

20522101
type DynamicDBRecord<Flag extends string> = ({ dynamicField: number } | { dynamicField: string }) & DBBoolTable<Flag>
2053-
>DynamicDBRecord : Symbol(DynamicDBRecord, Decl(keyofAndIndexedAccess.ts, 578, 1))
2054-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 580, 21))
2055-
>dynamicField : Symbol(dynamicField, Decl(keyofAndIndexedAccess.ts, 580, 46))
2056-
>dynamicField : Symbol(dynamicField, Decl(keyofAndIndexedAccess.ts, 580, 73))
2057-
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 565, 1))
2058-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 580, 21))
2102+
>DynamicDBRecord : Symbol(DynamicDBRecord, Decl(keyofAndIndexedAccess.ts, 587, 1))
2103+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 589, 21))
2104+
>dynamicField : Symbol(dynamicField, Decl(keyofAndIndexedAccess.ts, 589, 46))
2105+
>dynamicField : Symbol(dynamicField, Decl(keyofAndIndexedAccess.ts, 589, 73))
2106+
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 574, 70))
2107+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 589, 21))
20592108

20602109
function getFlagsFromDynamicRecord<Flag extends string>(record: DynamicDBRecord<Flag>, flags: Flag[]) {
2061-
>getFlagsFromDynamicRecord : Symbol(getFlagsFromDynamicRecord, Decl(keyofAndIndexedAccess.ts, 580, 117))
2062-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 581, 35))
2063-
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 581, 56))
2064-
>DynamicDBRecord : Symbol(DynamicDBRecord, Decl(keyofAndIndexedAccess.ts, 578, 1))
2065-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 581, 35))
2066-
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 581, 86))
2067-
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 581, 35))
2110+
>getFlagsFromDynamicRecord : Symbol(getFlagsFromDynamicRecord, Decl(keyofAndIndexedAccess.ts, 589, 117))
2111+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 590, 35))
2112+
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 590, 56))
2113+
>DynamicDBRecord : Symbol(DynamicDBRecord, Decl(keyofAndIndexedAccess.ts, 587, 1))
2114+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 590, 35))
2115+
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 590, 86))
2116+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 590, 35))
20682117

20692118
return record[flags[0]];
2070-
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 581, 56))
2071-
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 581, 86))
2119+
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 590, 56))
2120+
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 590, 86))
20722121
}
20732122

tests/baselines/reference/keyofAndIndexedAccess.types

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2345,6 +2345,55 @@ type Predicates<TaggedRecord> = {
23452345
>T : T
23462346
}
23472347

2348+
// Repros from #23592
2349+
2350+
type Example<T extends { [K in keyof T]: { prop: any } }> = { [K in keyof T]: T[K]["prop"] };
2351+
>Example : Example<T>
2352+
>T : T
2353+
>K : K
2354+
>T : T
2355+
>prop : any
2356+
>K : K
2357+
>T : T
2358+
>T : T
2359+
>K : K
2360+
2361+
type Result = Example<{ a: { prop: string }; b: { prop: number } }>;
2362+
>Result : Example<{ a: { prop: string; }; b: { prop: number; }; }>
2363+
>Example : Example<T>
2364+
>a : { prop: string; }
2365+
>prop : string
2366+
>b : { prop: number; }
2367+
>prop : number
2368+
2369+
type Helper2<T> = { [K in keyof T]: Extract<T[K], { prop: any }> };
2370+
>Helper2 : Helper2<T>
2371+
>T : T
2372+
>K : K
2373+
>T : T
2374+
>Extract : Extract<T, U>
2375+
>T : T
2376+
>K : K
2377+
>prop : any
2378+
2379+
type Example2<T> = { [K in keyof Helper2<T>]: Helper2<T>[K]["prop"] };
2380+
>Example2 : Example2<T>
2381+
>T : T
2382+
>K : K
2383+
>Helper2 : Helper2<T>
2384+
>T : T
2385+
>Helper2 : Helper2<T>
2386+
>T : T
2387+
>K : K
2388+
2389+
type Result2 = Example2<{ 1: { prop: string }; 2: { prop: number } }>;
2390+
>Result2 : Example2<{ 1: { prop: string; }; 2: { prop: number; }; }>
2391+
>Example2 : Example2<T>
2392+
>1 : { prop: string; }
2393+
>prop : string
2394+
>2 : { prop: number; }
2395+
>prop : number
2396+
23482397
// Repro from #23618
23492398

23502399
type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }

tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,15 @@ type Predicates<TaggedRecord> = {
568568
[T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T]
569569
}
570570

571+
// Repros from #23592
572+
573+
type Example<T extends { [K in keyof T]: { prop: any } }> = { [K in keyof T]: T[K]["prop"] };
574+
type Result = Example<{ a: { prop: string }; b: { prop: number } }>;
575+
576+
type Helper2<T> = { [K in keyof T]: Extract<T[K], { prop: any }> };
577+
type Example2<T> = { [K in keyof Helper2<T>]: Helper2<T>[K]["prop"] };
578+
type Result2 = Example2<{ 1: { prop: string }; 2: { prop: number } }>;
579+
571580
// Repro from #23618
572581

573582
type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }

0 commit comments

Comments
 (0)