Skip to content

Commit d7160c7

Browse files
authored
Merge pull request #13338 from Microsoft/fixCircularConstraints
Improve handling of circular constraints
2 parents 46938e0 + 81e8918 commit d7160c7

15 files changed

+1042
-365
lines changed

src/compiler/checker.ts

Lines changed: 101 additions & 75 deletions
Large diffs are not rendered by default.

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2806,7 +2806,7 @@ namespace ts {
28062806
EnumLike = Enum | EnumLiteral,
28072807
UnionOrIntersection = Union | Intersection,
28082808
StructuredType = Object | Union | Intersection,
2809-
StructuredOrTypeParameter = StructuredType | TypeParameter | Index,
2809+
StructuredOrTypeVariable = StructuredType | TypeParameter | Index | IndexedAccess,
28102810
TypeVariable = TypeParameter | IndexedAccess,
28112811

28122812
// 'Narrowable' types are types where narrowing actually narrows.

tests/baselines/reference/circularIndexedAccessErrors.errors.txt

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(3,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
22
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(7,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
3-
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(15,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
43
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(19,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
54
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(23,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
6-
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(27,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
7-
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(28,5): error TS2502: 'y' is referenced directly or indirectly in its own type annotation.
8-
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(29,5): error TS2502: 'z' is referenced directly or indirectly in its own type annotation.
5+
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(38,24): error TS2313: Type parameter 'T' has a circular constraint.
6+
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(38,30): error TS2536: Type '"hello"' cannot be used to index type 'T'.
97

108

11-
==== tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts (8 errors) ====
9+
==== tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts (6 errors) ====
1210

1311
type T1 = {
1412
x: T1["x"]; // Error
@@ -27,9 +25,7 @@ tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(29,5): error
2725
let x2x = x2.x;
2826

2927
interface T3<T extends T3<T>> {
30-
x: T["x"]; // Error
31-
~~~~~~~~~~
32-
!!! error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
28+
x: T["x"];
3329
}
3430

3531
interface T4<T extends T4<T>> {
@@ -45,13 +41,21 @@ tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(29,5): error
4541
}
4642

4743
class C2 {
48-
x: this["y"]; // Error
49-
~~~~~~~~~~~~~
50-
!!! error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
51-
y: this["z"]; // Error
52-
~~~~~~~~~~~~~
53-
!!! error TS2502: 'y' is referenced directly or indirectly in its own type annotation.
54-
z: this["x"]; // Error
55-
~~~~~~~~~~~~~
56-
!!! error TS2502: 'z' is referenced directly or indirectly in its own type annotation.
57-
}
44+
x: this["y"];
45+
y: this["z"];
46+
z: this["x"];
47+
}
48+
49+
// Repro from #12627
50+
51+
interface Foo {
52+
hello: boolean;
53+
}
54+
55+
function foo<T extends Foo | T["hello"]>() {
56+
~~~~~~~~~~~~~~~~
57+
!!! error TS2313: Type parameter 'T' has a circular constraint.
58+
~~~~~~~~~~
59+
!!! error TS2536: Type '"hello"' cannot be used to index type 'T'.
60+
}
61+

tests/baselines/reference/circularIndexedAccessErrors.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ declare let x2: T2<"x">;
1313
let x2x = x2.x;
1414

1515
interface T3<T extends T3<T>> {
16-
x: T["x"]; // Error
16+
x: T["x"];
1717
}
1818

1919
interface T4<T extends T4<T>> {
@@ -25,10 +25,20 @@ class C1 {
2525
}
2626

2727
class C2 {
28-
x: this["y"]; // Error
29-
y: this["z"]; // Error
30-
z: this["x"]; // Error
31-
}
28+
x: this["y"];
29+
y: this["z"];
30+
z: this["x"];
31+
}
32+
33+
// Repro from #12627
34+
35+
interface Foo {
36+
hello: boolean;
37+
}
38+
39+
function foo<T extends Foo | T["hello"]>() {
40+
}
41+
3242

3343
//// [circularIndexedAccessErrors.js]
3444
var x2x = x2.x;
@@ -42,6 +52,8 @@ var C2 = (function () {
4252
}
4353
return C2;
4454
}());
55+
function foo() {
56+
}
4557

4658

4759
//// [circularIndexedAccessErrors.d.ts]
@@ -68,3 +80,7 @@ declare class C2 {
6880
y: this["z"];
6981
z: this["x"];
7082
}
83+
interface Foo {
84+
hello: boolean;
85+
}
86+
declare function foo<T extends Foo | T["hello"]>(): void;

tests/baselines/reference/keyofAndIndexedAccess.js

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,30 @@ class C1 {
296296
}
297297
}
298298

299+
type S2 = {
300+
a: string;
301+
b: string;
302+
};
303+
304+
function f90<T extends S2, K extends keyof S2>(x1: S2[keyof S2], x2: T[keyof S2], x3: S2[K], x4: T[K]) {
305+
x1 = x2;
306+
x1 = x3;
307+
x1 = x4;
308+
x2 = x1;
309+
x2 = x3;
310+
x2 = x4;
311+
x3 = x1;
312+
x3 = x2;
313+
x3 = x4;
314+
x4 = x1;
315+
x4 = x2;
316+
x4 = x3;
317+
x1.length;
318+
x2.length;
319+
x3.length;
320+
x4.length;
321+
}
322+
299323
// Repros from #12011
300324

301325
class Base {
@@ -429,7 +453,44 @@ type SomeMethodDescriptor = {
429453
returnValue: string[];
430454
}
431455

432-
let result = dispatchMethod<SomeMethodDescriptor>("someMethod", ["hello", 35]);
456+
let result = dispatchMethod<SomeMethodDescriptor>("someMethod", ["hello", 35]);
457+
458+
// Repro from #13073
459+
460+
type KeyTypes = "a" | "b"
461+
let MyThingy: { [key in KeyTypes]: string[] };
462+
463+
function addToMyThingy<S extends KeyTypes>(key: S) {
464+
MyThingy[key].push("a");
465+
}
466+
467+
// Repro from #13285
468+
469+
function updateIds<T extends Record<K, string>, K extends string>(
470+
obj: T,
471+
idFields: K[],
472+
idMapping: { [oldId: string]: string }
473+
): Record<K, string> {
474+
for (const idField of idFields) {
475+
const newId = idMapping[obj[idField]];
476+
if (newId) {
477+
obj[idField] = newId;
478+
}
479+
}
480+
return obj;
481+
}
482+
483+
// Repro from #13285
484+
485+
function updateIds2<T extends { [x: string]: string }, K extends keyof T>(
486+
obj: T,
487+
key: K,
488+
stringMap: { [oldId: string]: string }
489+
) {
490+
var x = obj[key];
491+
stringMap[x]; // Should be OK.
492+
}
493+
433494

434495
//// [keyofAndIndexedAccess.js]
435496
var __extends = (this && this.__extends) || (function () {
@@ -649,6 +710,24 @@ var C1 = (function () {
649710
};
650711
return C1;
651712
}());
713+
function f90(x1, x2, x3, x4) {
714+
x1 = x2;
715+
x1 = x3;
716+
x1 = x4;
717+
x2 = x1;
718+
x2 = x3;
719+
x2 = x4;
720+
x3 = x1;
721+
x3 = x2;
722+
x3 = x4;
723+
x4 = x1;
724+
x4 = x2;
725+
x4 = x3;
726+
x1.length;
727+
x2.length;
728+
x3.length;
729+
x4.length;
730+
}
652731
// Repros from #12011
653732
var Base = (function () {
654733
function Base() {
@@ -718,6 +797,26 @@ function f(p) {
718797
a[p].add; // any
719798
}
720799
var result = dispatchMethod("someMethod", ["hello", 35]);
800+
var MyThingy;
801+
function addToMyThingy(key) {
802+
MyThingy[key].push("a");
803+
}
804+
// Repro from #13285
805+
function updateIds(obj, idFields, idMapping) {
806+
for (var _i = 0, idFields_1 = idFields; _i < idFields_1.length; _i++) {
807+
var idField = idFields_1[_i];
808+
var newId = idMapping[obj[idField]];
809+
if (newId) {
810+
obj[idField] = newId;
811+
}
812+
}
813+
return obj;
814+
}
815+
// Repro from #13285
816+
function updateIds2(obj, key, stringMap) {
817+
var x = obj[key];
818+
stringMap[x]; // Should be OK.
819+
}
721820

722821

723822
//// [keyofAndIndexedAccess.d.ts]
@@ -853,6 +952,11 @@ declare class C1 {
853952
set<K extends keyof this>(key: K, value: this[K]): void;
854953
foo(): void;
855954
}
955+
declare type S2 = {
956+
a: string;
957+
b: string;
958+
};
959+
declare function f90<T extends S2, K extends keyof S2>(x1: S2[keyof S2], x2: T[keyof S2], x3: S2[K], x4: T[K]): void;
856960
declare class Base {
857961
get<K extends keyof this>(prop: K): this[K];
858962
set<K extends keyof this>(prop: K, value: this[K]): void;
@@ -925,3 +1029,16 @@ declare type SomeMethodDescriptor = {
9251029
returnValue: string[];
9261030
};
9271031
declare let result: string[];
1032+
declare type KeyTypes = "a" | "b";
1033+
declare let MyThingy: {
1034+
[key in KeyTypes]: string[];
1035+
};
1036+
declare function addToMyThingy<S extends KeyTypes>(key: S): void;
1037+
declare function updateIds<T extends Record<K, string>, K extends string>(obj: T, idFields: K[], idMapping: {
1038+
[oldId: string]: string;
1039+
}): Record<K, string>;
1040+
declare function updateIds2<T extends {
1041+
[x: string]: string;
1042+
}, K extends keyof T>(obj: T, key: K, stringMap: {
1043+
[oldId: string]: string;
1044+
}): void;

0 commit comments

Comments
 (0)