Skip to content

Commit e1fda83

Browse files
authored
Cache complex union and intersection relations (microsoft#37910)
* Cache complex union/intersection relations * Accept new baselines * Accept new baselines
1 parent 9b17186 commit e1fda83

7 files changed

+56
-108
lines changed

src/compiler/checker.ts

+44-32
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ namespace ts {
202202
Source = 1 << 0,
203203
Target = 1 << 1,
204204
PropertyCheck = 1 << 2,
205-
InPropertyCheck = 1 << 3,
205+
UnionIntersectionCheck = 1 << 3,
206+
InPropertyCheck = 1 << 4,
206207
}
207208

208209
const enum MappedTypeModifiers {
@@ -17010,38 +17011,14 @@ namespace ts {
1701017011
// Note that these checks are specifically ordered to produce correct results. In particular,
1701117012
// we need to deconstruct unions before intersections (because unions are always at the top),
1701217013
// and we need to handle "each" relations before "some" relations for the same kind of type.
17013-
if (source.flags & TypeFlags.Union) {
17014-
result = relation === comparableRelation ?
17015-
someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState) :
17016-
eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState);
17014+
if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
17015+
result = getConstituentCount(source) * getConstituentCount(target) >= 4 ?
17016+
recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck) :
17017+
structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck);
1701717018
}
17018-
else {
17019-
if (target.flags & TypeFlags.Union) {
17020-
result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), <UnionType>target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
17021-
}
17022-
else if (target.flags & TypeFlags.Intersection) {
17023-
result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
17024-
}
17025-
else if (source.flags & TypeFlags.Intersection) {
17026-
// Check to see if any constituents of the intersection are immediately related to the target.
17027-
//
17028-
// Don't report errors though. Checking whether a constituent is related to the source is not actually
17029-
// useful and leads to some confusing error messages. Instead it is better to let the below checks
17030-
// take care of this, or to not elaborate at all. For instance,
17031-
//
17032-
// - For an object type (such as 'C = A & B'), users are usually more interested in structural errors.
17033-
//
17034-
// - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection
17035-
// than to report that 'D' is not assignable to 'A' or 'B'.
17036-
//
17037-
// - For a primitive type or type parameter (such as 'number = A & B') there is no point in
17038-
// breaking the intersection apart.
17039-
result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false, IntersectionState.Source);
17040-
}
17041-
if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) {
17042-
if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState)) {
17043-
resetErrorInfo(saveErrorInfo);
17044-
}
17019+
if (!result && !(source.flags & TypeFlags.Union) && (source.flags & (TypeFlags.StructuredOrInstantiable) || target.flags & TypeFlags.StructuredOrInstantiable)) {
17020+
if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState)) {
17021+
resetErrorInfo(saveErrorInfo);
1704517022
}
1704617023
}
1704717024
if (!result && source.flags & (TypeFlags.Intersection | TypeFlags.TypeParameter)) {
@@ -17549,6 +17526,37 @@ namespace ts {
1754917526
if (intersectionState & IntersectionState.PropertyCheck) {
1755017527
return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
1755117528
}
17529+
if (intersectionState & IntersectionState.UnionIntersectionCheck) {
17530+
// Note that these checks are specifically ordered to produce correct results. In particular,
17531+
// we need to deconstruct unions before intersections (because unions are always at the top),
17532+
// and we need to handle "each" relations before "some" relations for the same kind of type.
17533+
if (source.flags & TypeFlags.Union) {
17534+
return relation === comparableRelation ?
17535+
someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck) :
17536+
eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck);
17537+
}
17538+
if (target.flags & TypeFlags.Union) {
17539+
return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), <UnionType>target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
17540+
}
17541+
if (target.flags & TypeFlags.Intersection) {
17542+
return typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
17543+
}
17544+
// Source is an intersection. Check to see if any constituents of the intersection are immediately related
17545+
// to the target.
17546+
//
17547+
// Don't report errors though. Checking whether a constituent is related to the source is not actually
17548+
// useful and leads to some confusing error messages. Instead it is better to let the below checks
17549+
// take care of this, or to not elaborate at all. For instance,
17550+
//
17551+
// - For an object type (such as 'C = A & B'), users are usually more interested in structural errors.
17552+
//
17553+
// - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection
17554+
// than to report that 'D' is not assignable to 'A' or 'B'.
17555+
//
17556+
// - For a primitive type or type parameter (such as 'number = A & B') there is no point in
17557+
// breaking the intersection apart.
17558+
return someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false, IntersectionState.Source);
17559+
}
1755217560
const flags = source.flags & target.flags;
1755317561
if (relation === identityRelation && !(flags & TypeFlags.Object)) {
1755417562
if (flags & TypeFlags.Index) {
@@ -21510,6 +21518,10 @@ namespace ts {
2151021518
return mappedTypes && getUnionType(mappedTypes, noReductions ? UnionReduction.None : UnionReduction.Literal);
2151121519
}
2151221520

21521+
function getConstituentCount(type: Type) {
21522+
return type.flags & TypeFlags.UnionOrIntersection ? (<UnionOrIntersectionType>type).types.length : 1;
21523+
}
21524+
2151321525
function extractTypesOfKind(type: Type, kind: TypeFlags) {
2151421526
return filterType(type, t => (t.flags & kind) !== 0);
2151521527
}

tests/baselines/reference/baseClassImprovedMismatchErrors.errors.txt

+12-12
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ tests/cases/compiler/baseClassImprovedMismatchErrors.ts(8,5): error TS2416: Prop
22
Type 'string | Derived' is not assignable to type 'string | Base'.
33
Type 'Derived' is not assignable to type 'string | Base'.
44
Type 'Derived' is not assignable to type 'Base'.
5-
Types of property 'n' are incompatible.
6-
Type 'string | Derived' is not assignable to type 'string | Base'.
7-
Type 'Derived' is not assignable to type 'string | Base'.
5+
The types returned by 'fn()' are incompatible between these types.
6+
Type 'string | number' is not assignable to type 'number'.
7+
Type 'string' is not assignable to type 'number'.
88
tests/cases/compiler/baseClassImprovedMismatchErrors.ts(9,5): error TS2416: Property 'fn' in type 'Derived' is not assignable to the same property in base type 'Base'.
99
Type '() => string | number' is not assignable to type '() => number'.
1010
Type 'string | number' is not assignable to type 'number'.
@@ -13,9 +13,9 @@ tests/cases/compiler/baseClassImprovedMismatchErrors.ts(14,5): error TS2416: Pro
1313
Type 'string | DerivedInterface' is not assignable to type 'string | Base'.
1414
Type 'DerivedInterface' is not assignable to type 'string | Base'.
1515
Type 'DerivedInterface' is not assignable to type 'Base'.
16-
Types of property 'n' are incompatible.
17-
Type 'string | DerivedInterface' is not assignable to type 'string | Base'.
18-
Type 'DerivedInterface' is not assignable to type 'string | Base'.
16+
The types returned by 'fn()' are incompatible between these types.
17+
Type 'string | number' is not assignable to type 'number'.
18+
Type 'string' is not assignable to type 'number'.
1919
tests/cases/compiler/baseClassImprovedMismatchErrors.ts(15,5): error TS2416: Property 'fn' in type 'DerivedInterface' is not assignable to the same property in base type 'Base'.
2020
Type '() => string | number' is not assignable to type '() => number'.
2121
Type 'string | number' is not assignable to type 'number'.
@@ -36,9 +36,9 @@ tests/cases/compiler/baseClassImprovedMismatchErrors.ts(15,5): error TS2416: Pro
3636
!!! error TS2416: Type 'string | Derived' is not assignable to type 'string | Base'.
3737
!!! error TS2416: Type 'Derived' is not assignable to type 'string | Base'.
3838
!!! error TS2416: Type 'Derived' is not assignable to type 'Base'.
39-
!!! error TS2416: Types of property 'n' are incompatible.
40-
!!! error TS2416: Type 'string | Derived' is not assignable to type 'string | Base'.
41-
!!! error TS2416: Type 'Derived' is not assignable to type 'string | Base'.
39+
!!! error TS2416: The types returned by 'fn()' are incompatible between these types.
40+
!!! error TS2416: Type 'string | number' is not assignable to type 'number'.
41+
!!! error TS2416: Type 'string' is not assignable to type 'number'.
4242
fn() {
4343
~~
4444
!!! error TS2416: Property 'fn' in type 'Derived' is not assignable to the same property in base type 'Base'.
@@ -55,9 +55,9 @@ tests/cases/compiler/baseClassImprovedMismatchErrors.ts(15,5): error TS2416: Pro
5555
!!! error TS2416: Type 'string | DerivedInterface' is not assignable to type 'string | Base'.
5656
!!! error TS2416: Type 'DerivedInterface' is not assignable to type 'string | Base'.
5757
!!! error TS2416: Type 'DerivedInterface' is not assignable to type 'Base'.
58-
!!! error TS2416: Types of property 'n' are incompatible.
59-
!!! error TS2416: Type 'string | DerivedInterface' is not assignable to type 'string | Base'.
60-
!!! error TS2416: Type 'DerivedInterface' is not assignable to type 'string | Base'.
58+
!!! error TS2416: The types returned by 'fn()' are incompatible between these types.
59+
!!! error TS2416: Type 'string | number' is not assignable to type 'number'.
60+
!!! error TS2416: Type 'string' is not assignable to type 'number'.
6161
fn() {
6262
~~
6363
!!! error TS2416: Property 'fn' in type 'DerivedInterface' is not assignable to the same property in base type 'Base'.

tests/baselines/reference/classPropertyErrorOnNameOnly.errors.txt

-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ tests/cases/compiler/classPropertyErrorOnNameOnly.ts(7,3): error TS2322: Type '(
33
Type 'undefined' is not assignable to type 'string'.
44
tests/cases/compiler/classPropertyErrorOnNameOnly.ts(24,7): error TS2322: Type '(val: Values) => "1" | "2" | "3" | "4" | "5" | undefined' is not assignable to type 'FuncType'.
55
Type 'string | undefined' is not assignable to type 'string'.
6-
Type 'undefined' is not assignable to type 'string'.
76

87

98
==== tests/cases/compiler/classPropertyErrorOnNameOnly.ts (2 errors) ====
@@ -38,7 +37,6 @@ tests/cases/compiler/classPropertyErrorOnNameOnly.ts(24,7): error TS2322: Type '
3837
~~~~~~~~~~~~
3938
!!! error TS2322: Type '(val: Values) => "1" | "2" | "3" | "4" | "5" | undefined' is not assignable to type 'FuncType'.
4039
!!! error TS2322: Type 'string | undefined' is not assignable to type 'string'.
41-
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
4240
switch (val) {
4341
case 1:
4442
return "1";

tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt

-14
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(64,33): error
2222
Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'.
2323
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(66,24): error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
2424
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(67,24): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
25-
Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'.
2625
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(73,5): error TS2536: Type 'keyof T | keyof U' cannot be used to index type 'T | U'.
2726
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(74,5): error TS2536: Type 'keyof T | keyof U' cannot be used to index type 'T | U'.
2827
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(82,5): error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
@@ -34,14 +33,8 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(82,5): error
3433
Type 'string | number | symbol' is not assignable to type 'keyof U'.
3534
Type 'string' is not assignable to type 'keyof U'.
3635
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'.
37-
Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
38-
Type 'keyof T' is not assignable to type 'keyof U'.
3936
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'.
40-
Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
41-
Type 'keyof T' is not assignable to type 'keyof U'.
4237
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'.
43-
Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
44-
Type 'keyof T' is not assignable to type 'keyof U'.
4538
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(103,9): error TS2322: Type 'Extract<keyof T, string>' is not assignable to type 'K'.
4639
'Extract<keyof T, string>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
4740
Type 'string & keyof T' is not assignable to type 'K'.
@@ -191,7 +184,6 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(142,5): error
191184
setProperty(shape, cond ? "name" : "size", 10); // Error
192185
~~~~~~~~~~~~~~~~~~~~~~
193186
!!! error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
194-
!!! error TS2345: Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'.
195187
}
196188

197189
function f20<T, U>(x: T | U, y: T & U, k1: keyof (T | U), k2: keyof T & keyof U, k3: keyof (T & U), k4: keyof T | keyof U) {
@@ -223,20 +215,14 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(142,5): error
223215
k1 = k4; // Error
224216
~~
225217
!!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
226-
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
227-
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof U'.
228218

229219
k2 = k1;
230220
k2 = k3; // Error
231221
~~
232222
!!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
233-
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
234-
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof U'.
235223
k2 = k4; // Error
236224
~~
237225
!!! error TS2322: Type 'keyof T | keyof U' is not assignable to type 'keyof T & keyof U'.
238-
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof T & keyof U'.
239-
!!! error TS2322: Type 'keyof T' is not assignable to type 'keyof U'.
240226

241227
k3 = k1;
242228
k3 = k2;

0 commit comments

Comments
 (0)