Skip to content

Commit 9c4d9da

Browse files
ahejlsbergmprobst
authored andcommitted
Always cache relations involving intersection types (microsoft#46523)
* Always cache relations involving intersection types * Accept new baselines * Add regression test
1 parent d839ea7 commit 9c4d9da

10 files changed

+180
-41
lines changed

src/compiler/checker.ts

+11-12
Original file line numberDiff line numberDiff line change
@@ -18226,13 +18226,12 @@ namespace ts {
1822618226
let result = Ternary.False;
1822718227
const saveErrorInfo = captureErrorCalculationState();
1822818228

18229-
// Note that these checks are specifically ordered to produce correct results. In particular,
18230-
// we need to deconstruct unions before intersections (because unions are always at the top),
18231-
// and we need to handle "each" relations before "some" relations for the same kind of type.
18232-
if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
18233-
result = getConstituentCount(source) * getConstituentCount(target) >= 4 ?
18234-
recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags) :
18235-
structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck);
18229+
if ((source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) && getConstituentCount(source) * getConstituentCount(target) < 4) {
18230+
// We skip caching when source or target is a union with no more than three constituents.
18231+
result = structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck);
18232+
}
18233+
else if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
18234+
result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags);
1823618235
}
1823718236
if (!result && !(source.flags & TypeFlags.Union) && (source.flags & (TypeFlags.StructuredOrInstantiable) || target.flags & TypeFlags.StructuredOrInstantiable)) {
1823818237
if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) {
@@ -18696,17 +18695,17 @@ namespace ts {
1869618695
const maybeStart = maybeCount;
1869718696
maybeKeys[maybeCount] = id;
1869818697
maybeCount++;
18698+
const saveExpandingFlags = expandingFlags;
1869918699
if (recursionFlags & RecursionFlags.Source) {
1870018700
sourceStack[sourceDepth] = source;
1870118701
sourceDepth++;
18702+
if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth)) expandingFlags |= ExpandingFlags.Source;
1870218703
}
1870318704
if (recursionFlags & RecursionFlags.Target) {
1870418705
targetStack[targetDepth] = target;
1870518706
targetDepth++;
18707+
if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth)) expandingFlags |= ExpandingFlags.Target;
1870618708
}
18707-
const saveExpandingFlags = expandingFlags;
18708-
if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth)) expandingFlags |= ExpandingFlags.Source;
18709-
if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth)) expandingFlags |= ExpandingFlags.Target;
1871018709
let originalHandler: typeof outofbandVarianceMarkerHandler;
1871118710
let propagatingVarianceFlags: RelationComparisonResult = 0;
1871218711
if (outofbandVarianceMarkerHandler) {
@@ -18732,13 +18731,13 @@ namespace ts {
1873218731
if (outofbandVarianceMarkerHandler) {
1873318732
outofbandVarianceMarkerHandler = originalHandler;
1873418733
}
18735-
expandingFlags = saveExpandingFlags;
1873618734
if (recursionFlags & RecursionFlags.Source) {
1873718735
sourceDepth--;
1873818736
}
1873918737
if (recursionFlags & RecursionFlags.Target) {
1874018738
targetDepth--;
1874118739
}
18740+
expandingFlags = saveExpandingFlags;
1874218741
if (result) {
1874318742
if (result === Ternary.True || (sourceDepth === 0 && targetDepth === 0)) {
1874418743
if (result === Ternary.True || result === Ternary.Maybe) {
@@ -23173,7 +23172,7 @@ namespace ts {
2317323172
}
2317423173

2317523174
function getConstituentCount(type: Type) {
23176-
return type.flags & TypeFlags.UnionOrIntersection ? (type as UnionOrIntersectionType).types.length : 1;
23175+
return type.flags & TypeFlags.Union ? (type as UnionType).types.length : 1;
2317723176
}
2317823177

2317923178
function extractTypesOfKind(type: Type, kind: TypeFlags) {

tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt

+16-20
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t
99
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
1010
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
1111
Type '"text"' is not assignable to type 'T & "text"'.
12-
Type '"text"' is not assignable to type 'T'.
13-
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
14-
Type 'T' is not assignable to type 'T & "text"'.
15-
Type '"text" | "email"' is not assignable to type 'T & "text"'.
16-
Type '"text"' is not assignable to type 'T & "text"'.
17-
Type '"text"' is not assignable to type 'T'.
18-
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
19-
Type 'T' is not assignable to type '"text"'.
20-
Type '"text" | "email"' is not assignable to type '"text"'.
21-
Type '"email"' is not assignable to type '"text"'.
12+
Type 'T' is not assignable to type 'T & "text"'.
13+
Type '"text" | "email"' is not assignable to type 'T & "text"'.
14+
Type '"text"' is not assignable to type 'T & "text"'.
15+
Type '"text"' is not assignable to type 'T'.
16+
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
17+
Type 'T' is not assignable to type '"text"'.
18+
Type '"text" | "email"' is not assignable to type '"text"'.
19+
Type '"email"' is not assignable to type '"text"'.
2220

2321

2422
==== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts (1 errors) ====
@@ -67,16 +65,14 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t
6765
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
6866
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
6967
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
70-
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
71-
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
72-
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
73-
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
74-
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
75-
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
76-
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
77-
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
78-
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
79-
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
68+
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
69+
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
70+
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
71+
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
72+
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
73+
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
74+
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
75+
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
8076
}
8177

8278
const newTextChannel = makeNewChannel('text');

tests/baselines/reference/deepComparisons.errors.txt

+20-1
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,23 @@ tests/cases/compiler/deepComparisons.ts(4,9): error TS2322: Type 'T[K1][K2]' is
4646

4747
function f3<U>() {
4848
let x: Foo1<U> = 0 as any as Bar<U>; // No error!
49-
}
49+
}
50+
51+
// Repro from #46500
52+
53+
type F<T> = {} & (
54+
T extends [any, ...any[]]
55+
? { [K in keyof T]?: F<T[K]> }
56+
: T extends any[]
57+
? F<T[number]>[]
58+
: T extends { [K: string]: any }
59+
? { [K in keyof T]?: F<T[K]> }
60+
: { x: string }
61+
);
62+
63+
declare function f<T = any>(): F<T>;
64+
65+
function g() {
66+
return f() as F<any>;
67+
}
68+

tests/baselines/reference/deepComparisons.js

+23-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,26 @@ type Foo2<T> = { x: Foo1<T> };
1717

1818
function f3<U>() {
1919
let x: Foo1<U> = 0 as any as Bar<U>; // No error!
20-
}
20+
}
21+
22+
// Repro from #46500
23+
24+
type F<T> = {} & (
25+
T extends [any, ...any[]]
26+
? { [K in keyof T]?: F<T[K]> }
27+
: T extends any[]
28+
? F<T[number]>[]
29+
: T extends { [K: string]: any }
30+
? { [K in keyof T]?: F<T[K]> }
31+
: { x: string }
32+
);
33+
34+
declare function f<T = any>(): F<T>;
35+
36+
function g() {
37+
return f() as F<any>;
38+
}
39+
2140

2241
//// [deepComparisons.js]
2342
function f1() {
@@ -31,3 +50,6 @@ function f2() {
3150
function f3() {
3251
var x = 0; // No error!
3352
}
53+
function g() {
54+
return f();
55+
}

tests/baselines/reference/deepComparisons.symbols

+54
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,57 @@ function f3<U>() {
8484
>Bar : Symbol(Bar, Decl(deepComparisons.ts, 6, 28))
8585
>U : Symbol(U, Decl(deepComparisons.ts, 16, 12))
8686
}
87+
88+
// Repro from #46500
89+
90+
type F<T> = {} & (
91+
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
92+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
93+
94+
T extends [any, ...any[]]
95+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
96+
97+
? { [K in keyof T]?: F<T[K]> }
98+
>K : Symbol(K, Decl(deepComparisons.ts, 24, 13))
99+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
100+
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
101+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
102+
>K : Symbol(K, Decl(deepComparisons.ts, 24, 13))
103+
104+
: T extends any[]
105+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
106+
107+
? F<T[number]>[]
108+
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
109+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
110+
111+
: T extends { [K: string]: any }
112+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
113+
>K : Symbol(K, Decl(deepComparisons.ts, 27, 27))
114+
115+
? { [K in keyof T]?: F<T[K]> }
116+
>K : Symbol(K, Decl(deepComparisons.ts, 28, 21))
117+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
118+
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
119+
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
120+
>K : Symbol(K, Decl(deepComparisons.ts, 28, 21))
121+
122+
: { x: string }
123+
>x : Symbol(x, Decl(deepComparisons.ts, 29, 19))
124+
125+
);
126+
127+
declare function f<T = any>(): F<T>;
128+
>f : Symbol(f, Decl(deepComparisons.ts, 30, 2))
129+
>T : Symbol(T, Decl(deepComparisons.ts, 32, 19))
130+
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
131+
>T : Symbol(T, Decl(deepComparisons.ts, 32, 19))
132+
133+
function g() {
134+
>g : Symbol(g, Decl(deepComparisons.ts, 32, 36))
135+
136+
return f() as F<any>;
137+
>f : Symbol(f, Decl(deepComparisons.ts, 30, 2))
138+
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
139+
}
140+

tests/baselines/reference/deepComparisons.types

+31
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,34 @@ function f3<U>() {
5656
>0 as any : any
5757
>0 : 0
5858
}
59+
60+
// Repro from #46500
61+
62+
type F<T> = {} & (
63+
>F : F<T>
64+
65+
T extends [any, ...any[]]
66+
? { [K in keyof T]?: F<T[K]> }
67+
: T extends any[]
68+
? F<T[number]>[]
69+
: T extends { [K: string]: any }
70+
>K : string
71+
72+
? { [K in keyof T]?: F<T[K]> }
73+
: { x: string }
74+
>x : string
75+
76+
);
77+
78+
declare function f<T = any>(): F<T>;
79+
>f : <T = any>() => F<T>
80+
81+
function g() {
82+
>g : () => F<any>
83+
84+
return f() as F<any>;
85+
>f() as F<any> : F<any>
86+
>f() : F<any>
87+
>f : <T = any>() => F<T>
88+
}
89+

tests/baselines/reference/intersectionAndUnionTypes.errors.txt

-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(20,1): e
55
tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(23,1): error TS2322: Type 'A | B' is not assignable to type '(A & B) | (C & D)'.
66
Type 'A' is not assignable to type '(A & B) | (C & D)'.
77
Type 'A' is not assignable to type 'A & B'.
8-
Type 'A' is not assignable to type 'B'.
98
tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(25,1): error TS2322: Type 'C | D' is not assignable to type '(A & B) | (C & D)'.
109
Type 'C' is not assignable to type '(A & B) | (C & D)'.
1110
Type 'C' is not assignable to type 'C & D'.
@@ -80,7 +79,6 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): e
8079
!!! error TS2322: Type 'A | B' is not assignable to type '(A & B) | (C & D)'.
8180
!!! error TS2322: Type 'A' is not assignable to type '(A & B) | (C & D)'.
8281
!!! error TS2322: Type 'A' is not assignable to type 'A & B'.
83-
!!! error TS2322: Type 'A' is not assignable to type 'B'.
8482
x = cnd; // Ok
8583
x = cod;
8684
~

tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt

+6
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(82,5): error
3333
Type 'string | number | symbol' is not assignable to type 'keyof U'.
3434
Type 'string' is not assignable to type 'keyof U'.
3535
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(83,5): error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
36+
Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
3637
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(86,5): error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
38+
Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
3739
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(87,5): error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
40+
Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
3841
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(103,9): error TS2322: Type 'Extract<keyof T, string>' is not assignable to type 'K'.
3942
'Extract<keyof T, string>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
4043
Type 'string & keyof T' is not assignable to type 'K'.
@@ -209,14 +212,17 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(142,5): error
209212
k1 = k4; // Error
210213
~~
211214
!!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
215+
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
212216

213217
k2 = k1;
214218
k2 = k3; // Error
215219
~~
216220
!!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
221+
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
217222
k2 = k4; // Error
218223
~~
219224
!!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
225+
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
220226

221227
k3 = k1;
222228
k3 = k2;

tests/baselines/reference/unionTypeCallSignatures6.errors.txt

-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@ tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(38,4): error TS2
66
tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(39,1): error TS2684: The 'this' context of type 'A & C & { f0: F0 | F3; f1: F1 | F3; f2: F1 | F4; f3: F3 | F4; f4: F3 | F5; }' is not assignable to method's 'this' of type 'B'.
77
Property 'b' is missing in type 'A & C & { f0: F0 | F3; f1: F1 | F3; f2: F1 | F4; f3: F3 | F4; f4: F3 | F5; }' but required in type 'B'.
88
tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(48,1): error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B'.
9-
Type 'void' is not assignable to type 'A'.
109
tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(55,1): error TS2769: No overload matches this call.
1110
Overload 1 of 2, '(this: A & B & C): void', gave the following error.
1211
The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B & C'.
1312
Type 'void' is not assignable to type 'A'.
1413
Overload 2 of 2, '(this: A & B): void', gave the following error.
1514
The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B'.
16-
Type 'void' is not assignable to type 'A'.
1715

1816

1917
==== tests/cases/conformance/types/union/unionTypeCallSignatures6.ts (6 errors) ====
@@ -79,7 +77,6 @@ tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(55,1): error TS2
7977
f3(); // error
8078
~~~~
8179
!!! error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B'.
82-
!!! error TS2684: Type 'void' is not assignable to type 'A'.
8380

8481
interface F7 {
8582
(this: A & B & C): void;
@@ -94,5 +91,4 @@ tests/cases/conformance/types/union/unionTypeCallSignatures6.ts(55,1): error TS2
9491
!!! error TS2769: Type 'void' is not assignable to type 'A'.
9592
!!! error TS2769: Overload 2 of 2, '(this: A & B): void', gave the following error.
9693
!!! error TS2769: The 'this' context of type 'void' is not assignable to method's 'this' of type 'A & B'.
97-
!!! error TS2769: Type 'void' is not assignable to type 'A'.
9894

tests/cases/compiler/deepComparisons.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,22 @@ type Foo2<T> = { x: Foo1<T> };
1616

1717
function f3<U>() {
1818
let x: Foo1<U> = 0 as any as Bar<U>; // No error!
19-
}
19+
}
20+
21+
// Repro from #46500
22+
23+
type F<T> = {} & (
24+
T extends [any, ...any[]]
25+
? { [K in keyof T]?: F<T[K]> }
26+
: T extends any[]
27+
? F<T[number]>[]
28+
: T extends { [K: string]: any }
29+
? { [K in keyof T]?: F<T[K]> }
30+
: { x: string }
31+
);
32+
33+
declare function f<T = any>(): F<T>;
34+
35+
function g() {
36+
return f() as F<any>;
37+
}

0 commit comments

Comments
 (0)