Skip to content

Commit 7748694

Browse files
authored
Relate non-augmenting subtypes without resorting to structural comparison (#43624)
* Relate non-augmenting array subtypes without resorting to structural comparison * Fix lint * Generalize performance enhancement * Cache results, feed through via getNormalizedType to remove error intermediates * Use newly freed up object flags to limit member setting, fix crash with those object flags * Move flags because there is no TypeFlags.Reference 🤦
1 parent 5e4fcfb commit 7748694

15 files changed

+68
-49
lines changed

src/compiler/checker.ts

+30-3
Original file line numberDiff line numberDiff line change
@@ -17232,12 +17232,13 @@ namespace ts {
1723217232

1723317233
function getNormalizedType(type: Type, writing: boolean): Type {
1723417234
while (true) {
17235-
const t = isFreshLiteralType(type) ? (<FreshableType>type).regularType :
17235+
let t = isFreshLiteralType(type) ? (<FreshableType>type).regularType :
1723617236
getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).node ? createTypeReference((<TypeReference>type).target, getTypeArguments(<TypeReference>type)) :
1723717237
type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) :
1723817238
type.flags & TypeFlags.Substitution ? writing ? (<SubstitutionType>type).baseType : (<SubstitutionType>type).substitute :
1723917239
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) :
1724017240
type;
17241+
t = getSingleBaseForNonAugmentingSubtype(t) || t;
1724117242
if (t === type) break;
1724217243
type = t;
1724317244
}
@@ -17739,8 +17740,10 @@ namespace ts {
1773917740

1774017741
function reportErrorResults(source: Type, target: Type, result: Ternary, isComparingJsxAttributes: boolean) {
1774117742
if (!result && reportErrors) {
17742-
source = originalSource.aliasSymbol ? originalSource : source;
17743-
target = originalTarget.aliasSymbol ? originalTarget : target;
17743+
const sourceHasBase = !!getSingleBaseForNonAugmentingSubtype(originalSource);
17744+
const targetHasBase = !!getSingleBaseForNonAugmentingSubtype(originalTarget);
17745+
source = (originalSource.aliasSymbol || sourceHasBase) ? originalSource : source;
17746+
target = (originalTarget.aliasSymbol || targetHasBase) ? originalTarget : target;
1774417747
let maybeSuppress = overrideNextErrorInfo > 0;
1774517748
if (maybeSuppress) {
1774617749
overrideNextErrorInfo--;
@@ -19993,6 +19996,30 @@ namespace ts {
1999319996
return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
1999419997
}
1999519998

19999+
function getSingleBaseForNonAugmentingSubtype(type: Type) {
20000+
if (!(getObjectFlags(type) & ObjectFlags.Reference) || !(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface)) {
20001+
return undefined;
20002+
}
20003+
if (getObjectFlags(type) & ObjectFlags.IdenticalBaseTypeCalculated) {
20004+
return getObjectFlags(type) & ObjectFlags.IdenticalBaseTypeExists ? (type as TypeReference).cachedEquivalentBaseType : undefined;
20005+
}
20006+
(type as TypeReference).objectFlags |= ObjectFlags.IdenticalBaseTypeCalculated;
20007+
const target = (type as TypeReference).target as InterfaceType;
20008+
const bases = getBaseTypes(target);
20009+
if (bases.length !== 1) {
20010+
return undefined;
20011+
}
20012+
if (getMembersOfSymbol(type.symbol).size) {
20013+
return undefined; // If the interface has any members, they may subtype members in the base, so we should do a full structural comparison
20014+
}
20015+
let instantiatedBase = !length(target.typeParameters) ? bases[0] : instantiateType(bases[0], createTypeMapper(target.typeParameters!, getTypeArguments(type as TypeReference).slice(0, target.typeParameters!.length)));
20016+
if (length(getTypeArguments(type as TypeReference)) > length(target.typeParameters)) {
20017+
instantiatedBase = getTypeWithThisArgument(instantiatedBase, last(getTypeArguments(type as TypeReference)));
20018+
}
20019+
(type as TypeReference).objectFlags |= ObjectFlags.IdenticalBaseTypeExists;
20020+
return (type as TypeReference).cachedEquivalentBaseType = instantiatedBase;
20021+
}
20022+
1999620023
function isEmptyArrayLiteralType(type: Type): boolean {
1999720024
const elementType = getElementTypeOfArrayType(type);
1999820025
return strictNullChecks ? elementType === implicitNeverType : elementType === undefinedWideningType;

src/compiler/types.ts

+7
Original file line numberDiff line numberDiff line change
@@ -5200,6 +5200,11 @@ namespace ts {
52005200
ObjectRestType = 1 << 23, // Originates in object rest declaration
52015201
/* @internal */
52025202
IsClassInstanceClone = 1 << 24, // Type is a clone of a class instance type
5203+
// Flags that require TypeFlags.Object and ObjectFlags.Reference
5204+
/* @internal */
5205+
IdenticalBaseTypeCalculated = 1 << 25, // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already
5206+
/* @internal */
5207+
IdenticalBaseTypeExists = 1 << 26, // has a defined cachedEquivalentBaseType member
52035208

52045209
// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
52055210
/* @internal */
@@ -5281,6 +5286,8 @@ namespace ts {
52815286
resolvedTypeArguments?: readonly Type[]; // Resolved type reference type arguments
52825287
/* @internal */
52835288
literalType?: TypeReference; // Clone of type with ObjectFlags.ArrayLiteral set
5289+
/* @internal */
5290+
cachedEquivalentBaseType?: Type; // Only set on references to class or interfaces with a single base type and no augmentations
52845291
}
52855292

52865293
export interface DeferredTypeReference extends TypeReference {

tests/baselines/reference/arrayLiterals3.errors.txt

+4-6
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(17,5): error
88
tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(33,5): error TS2322: Type 'number[]' is not assignable to type '[number, number, number]'.
99
Target requires 3 element(s) but source may have fewer.
1010
tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error TS2322: Type '(string | number)[]' is not assignable to type 'myArray'.
11-
The types returned by 'pop()' are incompatible between these types.
12-
Type 'string | number' is not assignable to type 'Number'.
13-
Type 'string' is not assignable to type 'Number'.
11+
Type 'string | number' is not assignable to type 'Number'.
12+
Type 'string' is not assignable to type 'Number'.
1413

1514

1615
==== tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts (7 errors) ====
@@ -65,7 +64,6 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error
6564
var c2: myArray = [...temp1, ...temp]; // Error cannot assign (number|string)[] to number[]
6665
~~
6766
!!! error TS2322: Type '(string | number)[]' is not assignable to type 'myArray'.
68-
!!! error TS2322: The types returned by 'pop()' are incompatible between these types.
69-
!!! error TS2322: Type 'string | number' is not assignable to type 'Number'.
70-
!!! error TS2322: Type 'string' is not assignable to type 'Number'.
67+
!!! error TS2322: Type 'string | number' is not assignable to type 'Number'.
68+
!!! error TS2322: Type 'string' is not assignable to type 'Number'.
7169

tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt

-12
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
1717
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(81,5): error TS2322: Type 'Base' is not assignable to type '{ foo: string; }'.
1818
Property 'foo' is private in type 'Base' but not in type '{ foo: string; }'.
1919
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(82,5): error TS2322: Type 'I' is not assignable to type '{ foo: string; }'.
20-
Property 'foo' is private in type 'I' but not in type '{ foo: string; }'.
2120
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(84,5): error TS2322: Type 'E' is not assignable to type '{ foo: string; }'.
2221
Property 'foo' is private in type 'E' but not in type '{ foo: string; }'.
2322
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(86,5): error TS2322: Type '{ foo: string; }' is not assignable to type 'Base'.
@@ -27,23 +26,18 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
2726
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(89,5): error TS2322: Type 'E' is not assignable to type 'Base'.
2827
Types have separate declarations of a private property 'foo'.
2928
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(92,5): error TS2322: Type '{ foo: string; }' is not assignable to type 'I'.
30-
Property 'foo' is private in type 'I' but not in type '{ foo: string; }'.
3129
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(94,5): error TS2322: Type 'D' is not assignable to type 'I'.
32-
Property 'foo' is private in type 'I' but not in type 'D'.
3330
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(95,5): error TS2322: Type 'E' is not assignable to type 'I'.
34-
Types have separate declarations of a private property 'foo'.
3531
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(99,5): error TS2322: Type 'Base' is not assignable to type 'D'.
3632
Property 'foo' is private in type 'Base' but not in type 'D'.
3733
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(100,5): error TS2322: Type 'I' is not assignable to type 'D'.
38-
Property 'foo' is private in type 'I' but not in type 'D'.
3934
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(101,5): error TS2322: Type 'E' is not assignable to type 'D'.
4035
Property 'foo' is private in type 'E' but not in type 'D'.
4136
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(103,5): error TS2322: Type '{ foo: string; }' is not assignable to type 'E'.
4237
Property 'foo' is private in type 'E' but not in type '{ foo: string; }'.
4338
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(104,5): error TS2322: Type 'Base' is not assignable to type 'E'.
4439
Types have separate declarations of a private property 'foo'.
4540
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(105,5): error TS2322: Type 'I' is not assignable to type 'E'.
46-
Types have separate declarations of a private property 'foo'.
4741
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(106,5): error TS2322: Type 'D' is not assignable to type 'E'.
4842
Property 'foo' is private in type 'E' but not in type 'D'.
4943

@@ -160,7 +154,6 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
160154
a = i; // error
161155
~
162156
!!! error TS2322: Type 'I' is not assignable to type '{ foo: string; }'.
163-
!!! error TS2322: Property 'foo' is private in type 'I' but not in type '{ foo: string; }'.
164157
a = d;
165158
a = e; // error
166159
~
@@ -185,16 +178,13 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
185178
i = a; // error
186179
~
187180
!!! error TS2322: Type '{ foo: string; }' is not assignable to type 'I'.
188-
!!! error TS2322: Property 'foo' is private in type 'I' but not in type '{ foo: string; }'.
189181
i = b;
190182
i = d; // error
191183
~
192184
!!! error TS2322: Type 'D' is not assignable to type 'I'.
193-
!!! error TS2322: Property 'foo' is private in type 'I' but not in type 'D'.
194185
i = e; // error
195186
~
196187
!!! error TS2322: Type 'E' is not assignable to type 'I'.
197-
!!! error TS2322: Types have separate declarations of a private property 'foo'.
198188
i = i;
199189

200190
d = a;
@@ -205,7 +195,6 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
205195
d = i; // error
206196
~
207197
!!! error TS2322: Type 'I' is not assignable to type 'D'.
208-
!!! error TS2322: Property 'foo' is private in type 'I' but not in type 'D'.
209198
d = e; // error
210199
~
211200
!!! error TS2322: Type 'E' is not assignable to type 'D'.
@@ -222,7 +211,6 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
222211
e = i; // errror
223212
~
224213
!!! error TS2322: Type 'I' is not assignable to type 'E'.
225-
!!! error TS2322: Types have separate declarations of a private property 'foo'.
226214
e = d; // errror
227215
~
228216
!!! error TS2322: Type 'D' is not assignable to type 'E'.

tests/baselines/reference/checkJsxChildrenProperty5.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
tests/cases/conformance/jsx/file.tsx(20,10): error TS2741: Property 'children' is missing in type '{ a: number; b: string; }' but required in type 'Prop'.
2-
tests/cases/conformance/jsx/file.tsx(25,9): error TS2740: Type 'Element' is missing the following properties from type 'Button': render, setState, forceUpdate, state, and 2 more.
2+
tests/cases/conformance/jsx/file.tsx(25,9): error TS2740: Type 'ReactElement<any>' is missing the following properties from type 'Button': render, setState, forceUpdate, state, and 2 more.
33
tests/cases/conformance/jsx/file.tsx(29,10): error TS2740: Type 'typeof Button' is missing the following properties from type 'Button': render, setState, forceUpdate, props, and 3 more.
44

55

@@ -33,7 +33,7 @@ tests/cases/conformance/jsx/file.tsx(29,10): error TS2740: Type 'typeof Button'
3333
<Comp a={10} b="hi">
3434
<Button />
3535
~~~~~~~~~~
36-
!!! error TS2740: Type 'Element' is missing the following properties from type 'Button': render, setState, forceUpdate, state, and 2 more.
36+
!!! error TS2740: Type 'ReactElement<any>' is missing the following properties from type 'Button': render, setState, forceUpdate, state, and 2 more.
3737
!!! related TS6500 tests/cases/conformance/jsx/file.tsx:6:5: The expected type comes from property 'children' which is declared here on type 'IntrinsicAttributes & Prop'
3838
</Comp>;
3939
let k2 =

tests/baselines/reference/classImplementsClass4.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
tests/cases/compiler/classImplementsClass4.ts(5,7): error TS2720: Class 'C' incorrectly implements class 'A'. Did you mean to extend 'A' and inherit its members as a subclass?
22
Property 'x' is missing in type 'C' but required in type 'A'.
3-
tests/cases/compiler/classImplementsClass4.ts(16,1): error TS2741: Property 'x' is missing in type 'C' but required in type 'C2'.
3+
tests/cases/compiler/classImplementsClass4.ts(16,1): error TS2741: Property 'x' is missing in type 'C' but required in type 'A'.
44

55

66
==== tests/cases/compiler/classImplementsClass4.ts (2 errors) ====
@@ -25,5 +25,5 @@ tests/cases/compiler/classImplementsClass4.ts(16,1): error TS2741: Property 'x'
2525
c = c2;
2626
c2 = c;
2727
~~
28-
!!! error TS2741: Property 'x' is missing in type 'C' but required in type 'C2'.
28+
!!! error TS2741: Property 'x' is missing in type 'C' but required in type 'A'.
2929
!!! related TS2728 tests/cases/compiler/classImplementsClass4.ts:2:13: 'x' is declared here.

tests/baselines/reference/crashInsourcePropertyIsRelatableToTargetProperty.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/crashInsourcePropertyIsRelatableToTargetProperty.ts(9,5): error TS2741: Property 'x' is missing in type '(x: "hi", items: string[]) => typeof foo' but required in type 'D'.
1+
tests/cases/compiler/crashInsourcePropertyIsRelatableToTargetProperty.ts(9,5): error TS2741: Property 'x' is missing in type '(x: "hi", items: string[]) => typeof foo' but required in type 'C'.
22

33

44
==== tests/cases/compiler/crashInsourcePropertyIsRelatableToTargetProperty.ts (1 errors) ====
@@ -12,6 +12,6 @@ tests/cases/compiler/crashInsourcePropertyIsRelatableToTargetProperty.ts(9,5): e
1212
}
1313
var a: D = foo("hi", []);
1414
~
15-
!!! error TS2741: Property 'x' is missing in type '(x: "hi", items: string[]) => typeof foo' but required in type 'D'.
15+
!!! error TS2741: Property 'x' is missing in type '(x: "hi", items: string[]) => typeof foo' but required in type 'C'.
1616
!!! related TS2728 tests/cases/compiler/crashInsourcePropertyIsRelatableToTargetProperty.ts:2:13: 'x' is declared here.
1717

tests/baselines/reference/implementingAnInterfaceExtendingClassWithProtecteds.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ tests/cases/conformance/interfaces/interfacesExtendingClasses/implementingAnInte
77
tests/cases/conformance/interfaces/interfacesExtendingClasses/implementingAnInterfaceExtendingClassWithProtecteds.ts(21,7): error TS2420: Class 'Bar4' incorrectly implements interface 'I'.
88
Property 'x' is protected but type 'Bar4' is not a class derived from 'Foo'.
99
tests/cases/conformance/interfaces/interfacesExtendingClasses/implementingAnInterfaceExtendingClassWithProtecteds.ts(26,7): error TS2420: Class 'Bar5' incorrectly implements interface 'I'.
10-
Property 'y' is missing in type 'Bar5' but required in type 'I'.
10+
Property 'y' is missing in type 'Foo' but required in type 'I'.
1111
tests/cases/conformance/interfaces/interfacesExtendingClasses/implementingAnInterfaceExtendingClassWithProtecteds.ts(29,7): error TS2420: Class 'Bar6' incorrectly implements interface 'I'.
1212
Property 'y' is protected in type 'Bar6' but public in type 'I'.
1313

@@ -54,7 +54,7 @@ tests/cases/conformance/interfaces/interfacesExtendingClasses/implementingAnInte
5454
class Bar5 extends Foo implements I { // error
5555
~~~~
5656
!!! error TS2420: Class 'Bar5' incorrectly implements interface 'I'.
57-
!!! error TS2420: Property 'y' is missing in type 'Bar5' but required in type 'I'.
57+
!!! error TS2420: Property 'y' is missing in type 'Foo' but required in type 'I'.
5858
!!! related TS2728 tests/cases/conformance/interfaces/interfacesExtendingClasses/implementingAnInterfaceExtendingClassWithProtecteds.ts:6:5: 'y' is declared here.
5959
}
6060

0 commit comments

Comments
 (0)