Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a9d8df2

Browse filesBrowse files
authored
Merge pull request microsoft#14825 from Microsoft/fixDeeplyNestedCheck
Fix deeply nested type check
2 parents 87c291e + f139a6c commit a9d8df2
Copy full SHA for a9d8df2

6 files changed

+100-18Lines changed: 100 additions & 18 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+19-17Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8277,8 +8277,8 @@ namespace ts {
82778277
maybeStack[depth].set(id, RelationComparisonResult.Succeeded);
82788278
depth++;
82798279
const saveExpandingFlags = expandingFlags;
8280-
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth)) expandingFlags |= 1;
8281-
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack, depth)) expandingFlags |= 2;
8280+
if (!(expandingFlags & 1) && isDeeplyNestedType(source, sourceStack, depth)) expandingFlags |= 1;
8281+
if (!(expandingFlags & 2) && isDeeplyNestedType(target, targetStack, depth)) expandingFlags |= 2;
82828282
let result: Ternary;
82838283
if (expandingFlags === 3) {
82848284
result = Ternary.Maybe;
@@ -8698,21 +8698,23 @@ namespace ts {
86988698
return false;
86998699
}
87008700

8701-
// Return true if the given type is part of a deeply nested chain of generic instantiations. We consider this to be the case
8702-
// when structural type comparisons have been started for 10 or more instantiations of the same generic type. It is possible,
8703-
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely expanding.
8704-
// Effectively, we will generate a false positive when two types are structurally equal to at least 10 levels, but unequal at
8705-
// some level beyond that.
8706-
function isDeeplyNestedGeneric(type: Type, stack: Type[], depth: number): boolean {
8707-
// We track type references (created by createTypeReference) and instantiated types (created by instantiateType)
8708-
if (getObjectFlags(type) & (ObjectFlags.Reference | ObjectFlags.Instantiated) && depth >= 5) {
8701+
// Return true if the given type is deeply nested. We consider this to be the case when structural type comparisons
8702+
// for 5 or more occurrences or instantiations of the type have been recorded on the given stack. It is possible,
8703+
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
8704+
// expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5
8705+
// levels, but unequal at some level beyond that.
8706+
function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
8707+
// We track all object types that have an associated symbol (representing the origin of the type)
8708+
if (depth >= 5 && type.flags & TypeFlags.Object) {
87098709
const symbol = type.symbol;
8710-
let count = 0;
8711-
for (let i = 0; i < depth; i++) {
8712-
const t = stack[i];
8713-
if (getObjectFlags(t) & (ObjectFlags.Reference | ObjectFlags.Instantiated) && t.symbol === symbol) {
8714-
count++;
8715-
if (count >= 5) return true;
8710+
if (symbol) {
8711+
let count = 0;
8712+
for (let i = 0; i < depth; i++) {
8713+
const t = stack[i];
8714+
if (t.flags & TypeFlags.Object && t.symbol === symbol) {
8715+
count++;
8716+
if (count >= 5) return true;
8717+
}
87168718
}
87178719
}
87188720
}
@@ -9455,7 +9457,7 @@ namespace ts {
94559457
if (isInProcess(source, target)) {
94569458
return;
94579459
}
9458-
if (isDeeplyNestedGeneric(source, sourceStack, depth) && isDeeplyNestedGeneric(target, targetStack, depth)) {
9460+
if (isDeeplyNestedType(source, sourceStack, depth) && isDeeplyNestedType(target, targetStack, depth)) {
94599461
return;
94609462
}
94619463
const key = source.id + "," + target.id;
Collapse file

‎src/compiler/types.ts‎

Copy file name to clipboardExpand all lines: src/compiler/types.ts
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3014,7 +3014,6 @@ namespace ts {
30143014
ObjectLiteral = 1 << 7, // Originates in an object literal
30153015
EvolvingArray = 1 << 8, // Evolving array type
30163016
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
3017-
NonPrimitive = 1 << 10, // NonPrimitive object type
30183017
ClassOrInterface = Class | Interface
30193018
}
30203019

Collapse file
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//// [deeplyNestedCheck.ts]
2+
// Repro from #14794
3+
4+
interface DataSnapshot<X = {}> {
5+
child(path: string): DataSnapshot;
6+
}
7+
8+
interface Snapshot<T> extends DataSnapshot {
9+
child<U extends keyof T>(path: U): Snapshot<T[U]>;
10+
}
11+
12+
13+
//// [deeplyNestedCheck.js]
14+
// Repro from #14794
Collapse file
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== tests/cases/compiler/deeplyNestedCheck.ts ===
2+
// Repro from #14794
3+
4+
interface DataSnapshot<X = {}> {
5+
>DataSnapshot : Symbol(DataSnapshot, Decl(deeplyNestedCheck.ts, 0, 0))
6+
>X : Symbol(X, Decl(deeplyNestedCheck.ts, 2, 23))
7+
8+
child(path: string): DataSnapshot;
9+
>child : Symbol(DataSnapshot.child, Decl(deeplyNestedCheck.ts, 2, 32))
10+
>path : Symbol(path, Decl(deeplyNestedCheck.ts, 3, 8))
11+
>DataSnapshot : Symbol(DataSnapshot, Decl(deeplyNestedCheck.ts, 0, 0))
12+
}
13+
14+
interface Snapshot<T> extends DataSnapshot {
15+
>Snapshot : Symbol(Snapshot, Decl(deeplyNestedCheck.ts, 4, 1))
16+
>T : Symbol(T, Decl(deeplyNestedCheck.ts, 6, 19))
17+
>DataSnapshot : Symbol(DataSnapshot, Decl(deeplyNestedCheck.ts, 0, 0))
18+
19+
child<U extends keyof T>(path: U): Snapshot<T[U]>;
20+
>child : Symbol(Snapshot.child, Decl(deeplyNestedCheck.ts, 6, 44))
21+
>U : Symbol(U, Decl(deeplyNestedCheck.ts, 7, 8))
22+
>T : Symbol(T, Decl(deeplyNestedCheck.ts, 6, 19))
23+
>path : Symbol(path, Decl(deeplyNestedCheck.ts, 7, 27))
24+
>U : Symbol(U, Decl(deeplyNestedCheck.ts, 7, 8))
25+
>Snapshot : Symbol(Snapshot, Decl(deeplyNestedCheck.ts, 4, 1))
26+
>T : Symbol(T, Decl(deeplyNestedCheck.ts, 6, 19))
27+
>U : Symbol(U, Decl(deeplyNestedCheck.ts, 7, 8))
28+
}
29+
Collapse file
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== tests/cases/compiler/deeplyNestedCheck.ts ===
2+
// Repro from #14794
3+
4+
interface DataSnapshot<X = {}> {
5+
>DataSnapshot : DataSnapshot<X>
6+
>X : X
7+
8+
child(path: string): DataSnapshot;
9+
>child : (path: string) => DataSnapshot<{}>
10+
>path : string
11+
>DataSnapshot : DataSnapshot<X>
12+
}
13+
14+
interface Snapshot<T> extends DataSnapshot {
15+
>Snapshot : Snapshot<T>
16+
>T : T
17+
>DataSnapshot : DataSnapshot<X>
18+
19+
child<U extends keyof T>(path: U): Snapshot<T[U]>;
20+
>child : <U extends keyof T>(path: U) => Snapshot<T[U]>
21+
>U : U
22+
>T : T
23+
>path : U
24+
>U : U
25+
>Snapshot : Snapshot<T>
26+
>T : T
27+
>U : U
28+
}
29+
Collapse file
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Repro from #14794
2+
3+
interface DataSnapshot<X = {}> {
4+
child(path: string): DataSnapshot;
5+
}
6+
7+
interface Snapshot<T> extends DataSnapshot {
8+
child<U extends keyof T>(path: U): Snapshot<T[U]>;
9+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.