Skip to content

Commit

Permalink
Track source and target relationship stack depth seperately, only inc…
Browse files Browse the repository at this point in the history
…rease on change in value (#41821)

* Track source and target relationship stack depth seperately, only increase on change in value

* Add baselines for test from #43485

* Bail on unwrapping conditional constraints on the source side when the source conditional is already known to be spooling out of control

* More usage of isDeeplyNestedType to block _specifically_ conditional recursion on only one side

* Negative cases of getNarrowedType that match the exact type should be filtered out, even when generic

* Add test and fix for #44404

* Swap to manually specifying left and right recursion

* Rename Left -> Source, Right -> Target

Co-authored-by: Andrew Branch <andrew@wheream.io>
  • Loading branch information
weswigham and andrewbranch committed Sep 30, 2021
1 parent 96f259d commit 612c92d
Show file tree
Hide file tree
Showing 25 changed files with 1,368 additions and 133 deletions.
269 changes: 148 additions & 121 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
tests/cases/compiler/conditionalTypeVarianceBigArrayConstraintsPerformance.ts(9,5): error TS2322: Type 'Stuff<U>' is not assignable to type 'Stuff<T>'.
Type 'U' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'U'.


==== tests/cases/compiler/conditionalTypeVarianceBigArrayConstraintsPerformance.ts (1 errors) ====
/// <reference path="/.lib/react16.d.ts" />

type Stuff<T> =
T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: any;

function F<T, U>(p1: Stuff<T>, p2: Stuff<U>) {
p1 = p2; // Error
~~
!!! error TS2322: Type 'Stuff<U>' is not assignable to type 'Stuff<T>'.
!!! error TS2322: Type 'U' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'U'.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//// [conditionalTypeVarianceBigArrayConstraintsPerformance.ts]
/// <reference path="/.lib/react16.d.ts" />

type Stuff<T> =
T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: any;

function F<T, U>(p1: Stuff<T>, p2: Stuff<U>) {
p1 = p2; // Error
}

//// [conditionalTypeVarianceBigArrayConstraintsPerformance.js]
/// <reference path="react16.d.ts" />
function F(p1, p2) {
p1 = p2; // Error
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
=== tests/cases/compiler/conditionalTypeVarianceBigArrayConstraintsPerformance.ts ===
/// <reference path="react16.d.ts" />

type Stuff<T> =
>Stuff : Symbol(Stuff, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 0, 0))
>T : Symbol(T, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 2, 11))

T extends keyof JSX.IntrinsicElements
>T : Symbol(T, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 2, 11))
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12))
>IntrinsicElements : Symbol(JSX.IntrinsicElements, Decl(react16.d.ts, 2514, 86))

? JSX.IntrinsicElements[T]
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12))
>IntrinsicElements : Symbol(JSX.IntrinsicElements, Decl(react16.d.ts, 2514, 86))
>T : Symbol(T, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 2, 11))

: any;

function F<T, U>(p1: Stuff<T>, p2: Stuff<U>) {
>F : Symbol(F, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 5, 14))
>T : Symbol(T, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 7, 11))
>U : Symbol(U, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 7, 13))
>p1 : Symbol(p1, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 7, 17))
>Stuff : Symbol(Stuff, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 0, 0))
>T : Symbol(T, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 7, 11))
>p2 : Symbol(p2, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 7, 30))
>Stuff : Symbol(Stuff, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 0, 0))
>U : Symbol(U, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 7, 13))

p1 = p2; // Error
>p1 : Symbol(p1, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 7, 17))
>p2 : Symbol(p2, Decl(conditionalTypeVarianceBigArrayConstraintsPerformance.ts, 7, 30))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
=== tests/cases/compiler/conditionalTypeVarianceBigArrayConstraintsPerformance.ts ===
/// <reference path="react16.d.ts" />

type Stuff<T> =
>Stuff : Stuff<T>

T extends keyof JSX.IntrinsicElements
>JSX : any

? JSX.IntrinsicElements[T]
>JSX : any

: any;

function F<T, U>(p1: Stuff<T>, p2: Stuff<U>) {
>F : <T, U>(p1: Stuff<T>, p2: Stuff<U>) => void
>p1 : Stuff<T>
>p2 : Stuff<U>

p1 = p2; // Error
>p1 = p2 : Stuff<U>
>p1 : Stuff<T>
>p2 : Stuff<U>
}
49 changes: 49 additions & 0 deletions tests/baselines/reference/deepComparisons.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
tests/cases/compiler/deepComparisons.ts(2,9): error TS2322: Type 'T' is not assignable to type 'Extract<T, string>'.
tests/cases/compiler/deepComparisons.ts(3,9): error TS2322: Type 'T[K1]' is not assignable to type 'Extract<T[K1], string>'.
Type 'T[keyof T]' is not assignable to type 'Extract<T[K1], string>'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'Extract<T[K1], string>'.
Type 'T[string]' is not assignable to type 'Extract<T[K1], string>'.
tests/cases/compiler/deepComparisons.ts(4,9): error TS2322: Type 'T[K1][K2]' is not assignable to type 'Extract<T[K1][K2], string>'.
Type 'T[K1][keyof T[K1]]' is not assignable to type 'Extract<T[K1][K2], string>'.
Type 'T[K1][string] | T[K1][number] | T[K1][symbol]' is not assignable to type 'Extract<T[K1][K2], string>'.
Type 'T[K1][string]' is not assignable to type 'Extract<T[K1][K2], string>'.
Type 'T[keyof T][string]' is not assignable to type 'Extract<T[K1][K2], string>'.
Type 'T[string][string] | T[number][string] | T[symbol][string]' is not assignable to type 'Extract<T[K1][K2], string>'.
Type 'T[string][string]' is not assignable to type 'Extract<T[K1][K2], string>'.


==== tests/cases/compiler/deepComparisons.ts (3 errors) ====
function f1<T, K1 extends keyof T, K2 extends keyof T[K1]>() {
let v1: Extract<T, string> = 0 as any as T; // Error
~~
!!! error TS2322: Type 'T' is not assignable to type 'Extract<T, string>'.
let v2: Extract<T[K1], string> = 0 as any as T[K1]; // Error
~~
!!! error TS2322: Type 'T[K1]' is not assignable to type 'Extract<T[K1], string>'.
!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'Extract<T[K1], string>'.
!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'Extract<T[K1], string>'.
!!! error TS2322: Type 'T[string]' is not assignable to type 'Extract<T[K1], string>'.
let v3: Extract<T[K1][K2], string> = 0 as any as T[K1][K2]; // No error
~~
!!! error TS2322: Type 'T[K1][K2]' is not assignable to type 'Extract<T[K1][K2], string>'.
!!! error TS2322: Type 'T[K1][keyof T[K1]]' is not assignable to type 'Extract<T[K1][K2], string>'.
!!! error TS2322: Type 'T[K1][string] | T[K1][number] | T[K1][symbol]' is not assignable to type 'Extract<T[K1][K2], string>'.
!!! error TS2322: Type 'T[K1][string]' is not assignable to type 'Extract<T[K1][K2], string>'.
!!! error TS2322: Type 'T[keyof T][string]' is not assignable to type 'Extract<T[K1][K2], string>'.
!!! error TS2322: Type 'T[string][string] | T[number][string] | T[symbol][string]' is not assignable to type 'Extract<T[K1][K2], string>'.
!!! error TS2322: Type 'T[string][string]' is not assignable to type 'Extract<T[K1][K2], string>'.
}

type Foo<T> = { x: Foo<T> };
type Bar<T> = { x: Bar<T[]> };

function f2<U>() {
let x: Foo<U> = 0 as any as Bar<U>; // Error, excessive stack depth
}

type Foo1<T> = { x: Foo2<T> };
type Foo2<T> = { x: Foo1<T> };

function f3<U>() {
let x: Foo1<U> = 0 as any as Bar<U>; // No error!
}
33 changes: 33 additions & 0 deletions tests/baselines/reference/deepComparisons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//// [deepComparisons.ts]
function f1<T, K1 extends keyof T, K2 extends keyof T[K1]>() {
let v1: Extract<T, string> = 0 as any as T; // Error
let v2: Extract<T[K1], string> = 0 as any as T[K1]; // Error
let v3: Extract<T[K1][K2], string> = 0 as any as T[K1][K2]; // No error
}

type Foo<T> = { x: Foo<T> };
type Bar<T> = { x: Bar<T[]> };

function f2<U>() {
let x: Foo<U> = 0 as any as Bar<U>; // Error, excessive stack depth
}

type Foo1<T> = { x: Foo2<T> };
type Foo2<T> = { x: Foo1<T> };

function f3<U>() {
let x: Foo1<U> = 0 as any as Bar<U>; // No error!
}

//// [deepComparisons.js]
function f1() {
var v1 = 0; // Error
var v2 = 0; // Error
var v3 = 0; // No error
}
function f2() {
var x = 0; // Error, excessive stack depth
}
function f3() {
var x = 0; // No error!
}
86 changes: 86 additions & 0 deletions tests/baselines/reference/deepComparisons.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
=== tests/cases/compiler/deepComparisons.ts ===
function f1<T, K1 extends keyof T, K2 extends keyof T[K1]>() {
>f1 : Symbol(f1, Decl(deepComparisons.ts, 0, 0))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))
>K1 : Symbol(K1, Decl(deepComparisons.ts, 0, 14))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))
>K2 : Symbol(K2, Decl(deepComparisons.ts, 0, 34))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))
>K1 : Symbol(K1, Decl(deepComparisons.ts, 0, 14))

let v1: Extract<T, string> = 0 as any as T; // Error
>v1 : Symbol(v1, Decl(deepComparisons.ts, 1, 7))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))

let v2: Extract<T[K1], string> = 0 as any as T[K1]; // Error
>v2 : Symbol(v2, Decl(deepComparisons.ts, 2, 7))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))
>K1 : Symbol(K1, Decl(deepComparisons.ts, 0, 14))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))
>K1 : Symbol(K1, Decl(deepComparisons.ts, 0, 14))

let v3: Extract<T[K1][K2], string> = 0 as any as T[K1][K2]; // No error
>v3 : Symbol(v3, Decl(deepComparisons.ts, 3, 7))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))
>K1 : Symbol(K1, Decl(deepComparisons.ts, 0, 14))
>K2 : Symbol(K2, Decl(deepComparisons.ts, 0, 34))
>T : Symbol(T, Decl(deepComparisons.ts, 0, 12))
>K1 : Symbol(K1, Decl(deepComparisons.ts, 0, 14))
>K2 : Symbol(K2, Decl(deepComparisons.ts, 0, 34))
}

type Foo<T> = { x: Foo<T> };
>Foo : Symbol(Foo, Decl(deepComparisons.ts, 4, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 6, 9))
>x : Symbol(x, Decl(deepComparisons.ts, 6, 15))
>Foo : Symbol(Foo, Decl(deepComparisons.ts, 4, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 6, 9))

type Bar<T> = { x: Bar<T[]> };
>Bar : Symbol(Bar, Decl(deepComparisons.ts, 6, 28))
>T : Symbol(T, Decl(deepComparisons.ts, 7, 9))
>x : Symbol(x, Decl(deepComparisons.ts, 7, 15))
>Bar : Symbol(Bar, Decl(deepComparisons.ts, 6, 28))
>T : Symbol(T, Decl(deepComparisons.ts, 7, 9))

function f2<U>() {
>f2 : Symbol(f2, Decl(deepComparisons.ts, 7, 30))
>U : Symbol(U, Decl(deepComparisons.ts, 9, 12))

let x: Foo<U> = 0 as any as Bar<U>; // Error, excessive stack depth
>x : Symbol(x, Decl(deepComparisons.ts, 10, 7))
>Foo : Symbol(Foo, Decl(deepComparisons.ts, 4, 1))
>U : Symbol(U, Decl(deepComparisons.ts, 9, 12))
>Bar : Symbol(Bar, Decl(deepComparisons.ts, 6, 28))
>U : Symbol(U, Decl(deepComparisons.ts, 9, 12))
}

type Foo1<T> = { x: Foo2<T> };
>Foo1 : Symbol(Foo1, Decl(deepComparisons.ts, 11, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 13, 10))
>x : Symbol(x, Decl(deepComparisons.ts, 13, 16))
>Foo2 : Symbol(Foo2, Decl(deepComparisons.ts, 13, 30))
>T : Symbol(T, Decl(deepComparisons.ts, 13, 10))

type Foo2<T> = { x: Foo1<T> };
>Foo2 : Symbol(Foo2, Decl(deepComparisons.ts, 13, 30))
>T : Symbol(T, Decl(deepComparisons.ts, 14, 10))
>x : Symbol(x, Decl(deepComparisons.ts, 14, 16))
>Foo1 : Symbol(Foo1, Decl(deepComparisons.ts, 11, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 14, 10))

function f3<U>() {
>f3 : Symbol(f3, Decl(deepComparisons.ts, 14, 30))
>U : Symbol(U, Decl(deepComparisons.ts, 16, 12))

let x: Foo1<U> = 0 as any as Bar<U>; // No error!
>x : Symbol(x, Decl(deepComparisons.ts, 17, 7))
>Foo1 : Symbol(Foo1, Decl(deepComparisons.ts, 11, 1))
>U : Symbol(U, Decl(deepComparisons.ts, 16, 12))
>Bar : Symbol(Bar, Decl(deepComparisons.ts, 6, 28))
>U : Symbol(U, Decl(deepComparisons.ts, 16, 12))
}
58 changes: 58 additions & 0 deletions tests/baselines/reference/deepComparisons.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
=== tests/cases/compiler/deepComparisons.ts ===
function f1<T, K1 extends keyof T, K2 extends keyof T[K1]>() {
>f1 : <T, K1 extends keyof T, K2 extends keyof T[K1]>() => void

let v1: Extract<T, string> = 0 as any as T; // Error
>v1 : Extract<T, string>
>0 as any as T : T
>0 as any : any
>0 : 0

let v2: Extract<T[K1], string> = 0 as any as T[K1]; // Error
>v2 : Extract<T[K1], string>
>0 as any as T[K1] : T[K1]
>0 as any : any
>0 : 0

let v3: Extract<T[K1][K2], string> = 0 as any as T[K1][K2]; // No error
>v3 : Extract<T[K1][K2], string>
>0 as any as T[K1][K2] : T[K1][K2]
>0 as any : any
>0 : 0
}

type Foo<T> = { x: Foo<T> };
>Foo : Foo<T>
>x : Foo<T>

type Bar<T> = { x: Bar<T[]> };
>Bar : Bar<T>
>x : Bar<T[]>

function f2<U>() {
>f2 : <U>() => void

let x: Foo<U> = 0 as any as Bar<U>; // Error, excessive stack depth
>x : Foo<U>
>0 as any as Bar<U> : Bar<U>
>0 as any : any
>0 : 0
}

type Foo1<T> = { x: Foo2<T> };
>Foo1 : Foo1<T>
>x : Foo2<T>

type Foo2<T> = { x: Foo1<T> };
>Foo2 : Foo2<T>
>x : Foo1<T>

function f3<U>() {
>f3 : <U>() => void

let x: Foo1<U> = 0 as any as Bar<U>; // No error!
>x : Foo1<U>
>0 as any as Bar<U> : Bar<U>
>0 as any : any
>0 : 0
}
28 changes: 28 additions & 0 deletions tests/baselines/reference/genericCapturingFunctionNarrowing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//// [genericCapturingFunctionNarrowing.ts]
function needsToNarrowTheType<First extends { foo: string }, Second extends { bar: string }, SubFirst extends First, SubFirstMore extends First & {other: string}>(thing: First | SubFirst | SubFirstMore | Second) {
if (hasAFoo(thing)) {
console.log(thing.foo);
}
else {
// I would expect this to work because the type should be narrowed in this branch to `Second`
console.log(thing.bar); // Error: Property 'bar' does not exist on type 'First | Second'.
}

function hasAFoo(value: First | Second): value is First {
return "foo" in value;
}
}

//// [genericCapturingFunctionNarrowing.js]
function needsToNarrowTheType(thing) {
if (hasAFoo(thing)) {
console.log(thing.foo);
}
else {
// I would expect this to work because the type should be narrowed in this branch to `Second`
console.log(thing.bar); // Error: Property 'bar' does not exist on type 'First | Second'.
}
function hasAFoo(value) {
return "foo" in value;
}
}
Loading

0 comments on commit 612c92d

Please sign in to comment.