Skip to content

Commit 20c9d3a

Browse files
committed
Always substitute indexed generic mapped type when getting constraint from indexed access
1 parent fc756eb commit 20c9d3a

File tree

6 files changed

+128
-9
lines changed

6 files changed

+128
-9
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13512,7 +13512,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1351213512
}
1351313513

1351413514
function getConstraintFromIndexedAccess(type: IndexedAccessType) {
13515-
if (isMappedTypeGenericIndexedAccess(type)) {
13515+
if (isMappedTypeGenericIndexedAccess(type) || isGenericMappedType(type.objectType)) {
1351613516
// For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic,
1351713517
// we substitute an instantiation of E where P is replaced with X.
1351813518
return substituteIndexedMappedType(type.objectType as MappedType, type.indexType);

tests/baselines/reference/mappedTypeConstraints2.errors.txt

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(10,11): error TS2322: Type 'Mapped2<K>[`get${K}`]' is not assignable to type '{ a: K; }'.
2-
Type 'Mapped2<K>[`get${string}`]' is not assignable to type '{ a: K; }'.
2+
Type '{ a: `get${K}`; }' is not assignable to type '{ a: K; }'.
3+
Types of property 'a' are incompatible.
4+
Type '`get${K}`' is not assignable to type 'K'.
5+
'`get${K}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
6+
Type '`get${string}`' is not assignable to type 'K'.
7+
'`get${string}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
38
tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(16,11): error TS2322: Type 'Mapped3<K>[Uppercase<K>]' is not assignable to type '{ a: K; }'.
4-
Type 'Mapped3<K>[Uppercase<string>]' is not assignable to type '{ a: K; }'.
5-
Type 'Mapped3<K>[string]' is not assignable to type '{ a: K; }'.
9+
Type '{ a: Uppercase<K>; }' is not assignable to type '{ a: K; }'.
10+
Types of property 'a' are incompatible.
11+
Type 'Uppercase<K>' is not assignable to type 'K'.
12+
'Uppercase<K>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
13+
Type 'Uppercase<string>' is not assignable to type 'K'.
14+
'Uppercase<string>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
15+
Type 'string' is not assignable to type 'K'.
16+
'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
617
tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2322: Type 'Foo<T>[`get${T}`]' is not assignable to type 'T'.
7-
'T' could be instantiated with an arbitrary type which could be unrelated to 'Foo<T>[`get${T}`]'.
18+
'Foo<T>[`get${T}`]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
19+
Type '`get${T}`' is not assignable to type 'T'.
20+
'`get${T}`' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
21+
Type '`get${string}`' is not assignable to type 'T'.
22+
'`get${string}`' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
823

924

1025
==== tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts (3 errors) ====
@@ -20,7 +35,12 @@ tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2
2035
const x: { a: K } = obj[key]; // Error
2136
~
2237
!!! error TS2322: Type 'Mapped2<K>[`get${K}`]' is not assignable to type '{ a: K; }'.
23-
!!! error TS2322: Type 'Mapped2<K>[`get${string}`]' is not assignable to type '{ a: K; }'.
38+
!!! error TS2322: Type '{ a: `get${K}`; }' is not assignable to type '{ a: K; }'.
39+
!!! error TS2322: Types of property 'a' are incompatible.
40+
!!! error TS2322: Type '`get${K}`' is not assignable to type 'K'.
41+
!!! error TS2322: '`get${K}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
42+
!!! error TS2322: Type '`get${string}`' is not assignable to type 'K'.
43+
!!! error TS2322: '`get${string}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
2444
}
2545

2646
type Mapped3<K extends string> = { [P in K as Uppercase<P>]: { a: P } };
@@ -29,8 +49,14 @@ tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2
2949
const x: { a: K } = obj[key]; // Error
3050
~
3151
!!! error TS2322: Type 'Mapped3<K>[Uppercase<K>]' is not assignable to type '{ a: K; }'.
32-
!!! error TS2322: Type 'Mapped3<K>[Uppercase<string>]' is not assignable to type '{ a: K; }'.
33-
!!! error TS2322: Type 'Mapped3<K>[string]' is not assignable to type '{ a: K; }'.
52+
!!! error TS2322: Type '{ a: Uppercase<K>; }' is not assignable to type '{ a: K; }'.
53+
!!! error TS2322: Types of property 'a' are incompatible.
54+
!!! error TS2322: Type 'Uppercase<K>' is not assignable to type 'K'.
55+
!!! error TS2322: 'Uppercase<K>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
56+
!!! error TS2322: Type 'Uppercase<string>' is not assignable to type 'K'.
57+
!!! error TS2322: 'Uppercase<string>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
58+
!!! error TS2322: Type 'string' is not assignable to type 'K'.
59+
!!! error TS2322: 'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
3460
}
3561

3662
// Repro from #47794
@@ -42,7 +68,11 @@ tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2
4268
const get = <T extends string>(t: T, foo: Foo<T>): T => foo[`get${t}`]; // Type 'Foo<T>[`get${T}`]' is not assignable to type 'T'
4369
~~~~~~~~~~~~~~
4470
!!! error TS2322: Type 'Foo<T>[`get${T}`]' is not assignable to type 'T'.
45-
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'Foo<T>[`get${T}`]'.
71+
!!! error TS2322: 'Foo<T>[`get${T}`]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
72+
!!! error TS2322: Type '`get${T}`' is not assignable to type 'T'.
73+
!!! error TS2322: '`get${T}`' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
74+
!!! error TS2322: Type '`get${string}`' is not assignable to type 'T'.
75+
!!! error TS2322: '`get${string}`' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
4676

4777
// Repro from #48626
4878

@@ -65,4 +95,14 @@ tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2
6595
}
6696
return true;
6797
}
98+
99+
// repro from #50030
100+
101+
type ObjectWithUnderscoredKeys<K extends string> = {
102+
[k in K as `_${k}`]: true;
103+
};
104+
105+
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
106+
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
107+
}
68108

tests/baselines/reference/mappedTypeConstraints2.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
4646
}
4747
return true;
4848
}
49+
50+
// repro from #50030
51+
52+
type ObjectWithUnderscoredKeys<K extends string> = {
53+
[k in K as `_${k}`]: true;
54+
};
55+
56+
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
57+
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
58+
}
4959

5060

5161
//// [mappedTypeConstraints2.js]
@@ -71,6 +81,9 @@ function validate(obj, bounds) {
7181
}
7282
return true;
7383
}
84+
function genericTest(objectWithUnderscoredKeys, key) {
85+
const shouldBeTrue = objectWithUnderscoredKeys[`_${key}`];
86+
}
7487

7588

7689
//// [mappedTypeConstraints2.d.ts]
@@ -104,3 +117,7 @@ type NumericBoundsOf<T> = {
104117
[K in keyof T as T[K] extends number | undefined ? K : never]: Bounds;
105118
};
106119
declare function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>): boolean;
120+
type ObjectWithUnderscoredKeys<K extends string> = {
121+
[k in K as `_${k}`]: true;
122+
};
123+
declare function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K): void;

tests/baselines/reference/mappedTypeConstraints2.symbols

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,31 @@ function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
171171
return true;
172172
}
173173

174+
// repro from #50030
175+
176+
type ObjectWithUnderscoredKeys<K extends string> = {
177+
>ObjectWithUnderscoredKeys : Symbol(ObjectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 46, 1))
178+
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 50, 31))
179+
180+
[k in K as `_${k}`]: true;
181+
>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 51, 5))
182+
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 50, 31))
183+
>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 51, 5))
184+
185+
};
186+
187+
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
188+
>genericTest : Symbol(genericTest, Decl(mappedTypeConstraints2.ts, 52, 2))
189+
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 54, 21))
190+
>objectWithUnderscoredKeys : Symbol(objectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 54, 39))
191+
>ObjectWithUnderscoredKeys : Symbol(ObjectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 46, 1))
192+
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 54, 21))
193+
>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 54, 95))
194+
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 54, 21))
195+
196+
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
197+
>shouldBeTrue : Symbol(shouldBeTrue, Decl(mappedTypeConstraints2.ts, 55, 7))
198+
>objectWithUnderscoredKeys : Symbol(objectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 54, 39))
199+
>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 54, 95))
200+
}
201+

tests/baselines/reference/mappedTypeConstraints2.types

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,27 @@ function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
128128
>true : true
129129
}
130130

131+
// repro from #50030
132+
133+
type ObjectWithUnderscoredKeys<K extends string> = {
134+
>ObjectWithUnderscoredKeys : ObjectWithUnderscoredKeys<K>
135+
136+
[k in K as `_${k}`]: true;
137+
>true : true
138+
139+
};
140+
141+
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
142+
>genericTest : <K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) => void
143+
>objectWithUnderscoredKeys : ObjectWithUnderscoredKeys<K>
144+
>key : K
145+
146+
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
147+
>shouldBeTrue : true
148+
>true : true
149+
>objectWithUnderscoredKeys[`_${key}`] : ObjectWithUnderscoredKeys<K>[`_${K}`]
150+
>objectWithUnderscoredKeys : ObjectWithUnderscoredKeys<K>
151+
>`_${key}` : `_${K}`
152+
>key : K
153+
}
154+

tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,13 @@ function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
4949
}
5050
return true;
5151
}
52+
53+
// repro from #50030
54+
55+
type ObjectWithUnderscoredKeys<K extends string> = {
56+
[k in K as `_${k}`]: true;
57+
};
58+
59+
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
60+
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
61+
}

0 commit comments

Comments
 (0)