Skip to content

Commit 439cdca

Browse files
authored
Merge pull request #17870 from Microsoft/fix-getConstraintOfIndexedAccess
Fix getConstraintOfIndexedAccess
2 parents ade3b56 + a187b17 commit 439cdca

9 files changed

+334
-8
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5911,7 +5911,13 @@ namespace ts {
59115911
return transformed;
59125912
}
59135913
const baseObjectType = getBaseConstraintOfType(type.objectType);
5914-
const baseIndexType = getBaseConstraintOfType(type.indexType);
5914+
const keepTypeParameterForMappedType = baseObjectType && getObjectFlags(baseObjectType) & ObjectFlags.Mapped && type.indexType.flags & TypeFlags.TypeParameter;
5915+
const baseIndexType = !keepTypeParameterForMappedType && getBaseConstraintOfType(type.indexType);
5916+
if (baseObjectType && baseIndexType === stringType && !getIndexInfoOfType(baseObjectType, IndexKind.String)) {
5917+
// getIndexedAccessType returns `any` for X[string] where X doesn't have an index signature.
5918+
// instead, return undefined so that the indexed access check fails
5919+
return undefined;
5920+
}
59155921
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
59165922
}
59175923

@@ -5961,8 +5967,9 @@ namespace ts {
59615967
function computeBaseConstraint(t: Type): Type {
59625968
if (t.flags & TypeFlags.TypeParameter) {
59635969
const constraint = getConstraintFromTypeParameter(<TypeParameter>t);
5964-
return (<TypeParameter>t).isThisType ? constraint :
5965-
constraint ? getBaseConstraint(constraint) : undefined;
5970+
return (t as TypeParameter).isThisType || !constraint ?
5971+
constraint :
5972+
getBaseConstraint(constraint);
59665973
}
59675974
if (t.flags & TypeFlags.UnionOrIntersection) {
59685975
const types = (<UnionOrIntersectionType>t).types;
@@ -5990,9 +5997,6 @@ namespace ts {
59905997
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
59915998
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
59925999
}
5993-
if (isGenericMappedType(t)) {
5994-
return emptyObjectType;
5995-
}
59966000
return t;
59976001
}
59986002
}
@@ -9289,7 +9293,7 @@ namespace ts {
92899293
}
92909294
else if (target.flags & TypeFlags.IndexedAccess) {
92919295
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
9292-
// A is the apparent type of S.
9296+
// A is the apparent type of T.
92939297
const constraint = getConstraintOfType(<IndexedAccessType>target);
92949298
if (constraint) {
92959299
if (result = isRelatedTo(source, constraint, reportErrors)) {

src/compiler/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ namespace ts {
718718
export function sum<T extends Record<K, number>, K extends string>(array: T[], prop: K): number {
719719
let result = 0;
720720
for (const v of array) {
721-
// Note: we need the following type assertion because of GH #17069
721+
// TODO: Remove the following type assertion once the fix for #17069 is merged
722722
result += v[prop] as number;
723723
}
724724
return result;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//// [additionOperatorWithConstrainedTypeParameter.ts]
2+
// test for #17069
3+
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
4+
n = n + v[k];
5+
n += v[k]; // += should work the same way
6+
}
7+
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
8+
for (const v of vs) {
9+
n = n + v[k];
10+
n += v[k];
11+
}
12+
}
13+
14+
15+
//// [additionOperatorWithConstrainedTypeParameter.js]
16+
// test for #17069
17+
function sum(n, v, k) {
18+
n = n + v[k];
19+
n += v[k]; // += should work the same way
20+
}
21+
function realSum(n, vs, k) {
22+
for (var _i = 0, vs_1 = vs; _i < vs_1.length; _i++) {
23+
var v = vs_1[_i];
24+
n = n + v[k];
25+
n += v[k];
26+
}
27+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
=== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithConstrainedTypeParameter.ts ===
2+
// test for #17069
3+
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
4+
>sum : Symbol(sum, Decl(additionOperatorWithConstrainedTypeParameter.ts, 0, 0))
5+
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 13))
6+
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
7+
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
8+
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
9+
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
10+
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
11+
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 13))
12+
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
13+
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
14+
15+
n = n + v[k];
16+
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
17+
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
18+
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
19+
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
20+
21+
n += v[k]; // += should work the same way
22+
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
23+
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
24+
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
25+
}
26+
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
27+
>realSum : Symbol(realSum, Decl(additionOperatorWithConstrainedTypeParameter.ts, 4, 1))
28+
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 17))
29+
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
30+
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
31+
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
32+
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
33+
>vs : Symbol(vs, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 74))
34+
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 17))
35+
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
36+
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
37+
38+
for (const v of vs) {
39+
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
40+
>vs : Symbol(vs, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 74))
41+
42+
n = n + v[k];
43+
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
44+
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
45+
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
46+
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
47+
48+
n += v[k];
49+
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
50+
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
51+
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
52+
}
53+
}
54+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
=== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithConstrainedTypeParameter.ts ===
2+
// test for #17069
3+
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
4+
>sum : <T extends Record<K, number>, K extends string>(n: number, v: T, k: K) => void
5+
>T : T
6+
>Record : Record<K, T>
7+
>K : K
8+
>K : K
9+
>n : number
10+
>v : T
11+
>T : T
12+
>k : K
13+
>K : K
14+
15+
n = n + v[k];
16+
>n = n + v[k] : number
17+
>n : number
18+
>n + v[k] : number
19+
>n : number
20+
>v[k] : T[K]
21+
>v : T
22+
>k : K
23+
24+
n += v[k]; // += should work the same way
25+
>n += v[k] : number
26+
>n : number
27+
>v[k] : T[K]
28+
>v : T
29+
>k : K
30+
}
31+
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
32+
>realSum : <T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) => void
33+
>T : T
34+
>Record : Record<K, T>
35+
>K : K
36+
>K : K
37+
>n : number
38+
>vs : T[]
39+
>T : T
40+
>k : K
41+
>K : K
42+
43+
for (const v of vs) {
44+
>v : T
45+
>vs : T[]
46+
47+
n = n + v[k];
48+
>n = n + v[k] : number
49+
>n : number
50+
>n + v[k] : number
51+
>n : number
52+
>v[k] : T[K]
53+
>v : T
54+
>k : K
55+
56+
n += v[k];
57+
>n += v[k] : number
58+
>n : number
59+
>v[k] : T[K]
60+
>v : T
61+
>k : K
62+
}
63+
}
64+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(3,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
2+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(6,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
3+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(9,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
4+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(12,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
5+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(15,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
6+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(18,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
7+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(21,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
8+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(24,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
9+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(27,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
10+
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(30,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
11+
Type 'string' is not assignable to type 'number'.
12+
13+
14+
==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts (10 errors) ====
15+
// test for #15371
16+
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
17+
tp = s;
18+
~~
19+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
20+
}
21+
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
22+
tp = s;
23+
~~
24+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
25+
}
26+
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
27+
tp = s;
28+
~~
29+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
30+
}
31+
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
32+
tp = s;
33+
~~
34+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
35+
}
36+
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
37+
tp = s;
38+
~~
39+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
40+
}
41+
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
42+
tp = s;
43+
~~
44+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
45+
}
46+
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
47+
tp = s;
48+
~~
49+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
50+
}
51+
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
52+
tp = s;
53+
~~
54+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
55+
}
56+
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
57+
tp = s;
58+
~~
59+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
60+
}
61+
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
62+
tp = s;
63+
~~
64+
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
65+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
66+
}
67+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//// [nonPrimitiveConstraintOfIndexAccessType.ts]
2+
// test for #15371
3+
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
4+
tp = s;
5+
}
6+
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
7+
tp = s;
8+
}
9+
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
10+
tp = s;
11+
}
12+
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
13+
tp = s;
14+
}
15+
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
16+
tp = s;
17+
}
18+
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
19+
tp = s;
20+
}
21+
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
22+
tp = s;
23+
}
24+
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
25+
tp = s;
26+
}
27+
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
28+
tp = s;
29+
}
30+
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
31+
tp = s;
32+
}
33+
34+
35+
//// [nonPrimitiveConstraintOfIndexAccessType.js]
36+
"use strict";
37+
// test for #15371
38+
function f(s, tp) {
39+
tp = s;
40+
}
41+
function g(s, tp) {
42+
tp = s;
43+
}
44+
function h(s, tp) {
45+
tp = s;
46+
}
47+
function i(s, tp) {
48+
tp = s;
49+
}
50+
function j(s, tp) {
51+
tp = s;
52+
}
53+
function k(s, tp) {
54+
tp = s;
55+
}
56+
function o(s, tp) {
57+
tp = s;
58+
}
59+
function l(s, tp) {
60+
tp = s;
61+
}
62+
function m(s, tp) {
63+
tp = s;
64+
}
65+
function n(s, tp) {
66+
tp = s;
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// test for #17069
2+
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
3+
n = n + v[k];
4+
n += v[k]; // += should work the same way
5+
}
6+
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
7+
for (const v of vs) {
8+
n = n + v[k];
9+
n += v[k];
10+
}
11+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// @strict: true
2+
// test for #15371
3+
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
4+
tp = s;
5+
}
6+
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
7+
tp = s;
8+
}
9+
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
10+
tp = s;
11+
}
12+
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
13+
tp = s;
14+
}
15+
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
16+
tp = s;
17+
}
18+
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
19+
tp = s;
20+
}
21+
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
22+
tp = s;
23+
}
24+
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
25+
tp = s;
26+
}
27+
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
28+
tp = s;
29+
}
30+
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
31+
tp = s;
32+
}

0 commit comments

Comments
 (0)