From 7c9dc3406f085be5e3947f7dad322d91e245a202 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 9 Apr 2021 15:21:33 -0700 Subject: [PATCH 1/6] Relate non-augmenting array subtypes without resorting to structural comparison --- src/compiler/checker.ts | 34 +++++++++++++++++++ .../reference/arrayLiterals3.errors.txt | 4 +-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 530e734a552a2..b601c7a9153dd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18219,6 +18219,22 @@ namespace ts { return result; } + if (isNonAugmentingArraySubtype(source) && isArrayType(target)) { + let sourceBase = getBaseTypes((source as TypeReference).target as InterfaceType)[0]; + if (length((target as TypeReference).resolvedTypeArguments) === 2) { + sourceBase = getTypeWithThisArgument(sourceBase, ((source as TypeReference).target as InterfaceType).thisType); + } + return isRelatedTo(sourceBase, target, reportErrors); + } + + if (isArrayType(source) && isNonAugmentingArraySubtype(target)) { + let targetBase = getBaseTypes((target as TypeReference).target as InterfaceType)[0]; + if (length((source as TypeReference).resolvedTypeArguments) === 2) { + targetBase = getTypeWithThisArgument(targetBase, ((target as TypeReference).target as InterfaceType).thisType); + } + return isRelatedTo(source, targetBase, reportErrors); + } + if (target.flags & TypeFlags.TypeParameter) { // A source type { [P in Q]: X } is related to a target type T if keyof T is related to Q and X is related to T[Q]. if (getObjectFlags(source) & ObjectFlags.Mapped && !(source).declaration.nameType && isRelatedTo(getIndexType(target), getConstraintTypeFromMappedType(source))) { @@ -19868,6 +19884,24 @@ namespace ts { return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } + function isNonAugmentingArraySubtype(type: Type) { + if (!(getObjectFlags(type) & ObjectFlags.Reference) || !(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface)) { + return false; + } + const target = (type as TypeReference).target as InterfaceType; + const bases = getBaseTypes(target); + if (bases.length !== 1) { + return false; + } + if (!isArrayType(bases[0])) { + return false; + } + if (getMembersOfSymbol(type.symbol).size) { + return false; // If the interface has any members, they may subtype members in the base, so we should do a full structural comparison + } + return true; + } + function isEmptyArrayLiteralType(type: Type): boolean { const elementType = getElementTypeOfArrayType(type); return strictNullChecks ? elementType === implicitNeverType : elementType === undefinedWideningType; diff --git a/tests/baselines/reference/arrayLiterals3.errors.txt b/tests/baselines/reference/arrayLiterals3.errors.txt index 8ea3d1583b6fb..782983eb9884d 100644 --- a/tests/baselines/reference/arrayLiterals3.errors.txt +++ b/tests/baselines/reference/arrayLiterals3.errors.txt @@ -8,7 +8,7 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(17,5): error tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(33,5): error TS2322: Type 'number[]' is not assignable to type '[number, number, number]'. Target requires 3 element(s) but source may have fewer. tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error TS2322: Type '(string | number)[]' is not assignable to type 'myArray'. - The types returned by 'pop()' are incompatible between these types. + Type '(string | number)[]' is not assignable to type 'Number[]'. Type 'string | number' is not assignable to type 'Number'. Type 'string' is not assignable to type 'Number'. @@ -65,7 +65,7 @@ tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts(34,5): error var c2: myArray = [...temp1, ...temp]; // Error cannot assign (number|string)[] to number[] ~~ !!! error TS2322: Type '(string | number)[]' is not assignable to type 'myArray'. -!!! error TS2322: The types returned by 'pop()' are incompatible between these types. +!!! error TS2322: Type '(string | number)[]' is not assignable to type 'Number[]'. !!! error TS2322: Type 'string | number' is not assignable to type 'Number'. !!! error TS2322: Type 'string' is not assignable to type 'Number'. \ No newline at end of file From f1cff54075ff298e5cba987c28ad3cd10a152e3e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 9 Apr 2021 15:32:05 -0700 Subject: [PATCH 2/6] Fix lint --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b601c7a9153dd..2929b688af71e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18221,7 +18221,7 @@ namespace ts { if (isNonAugmentingArraySubtype(source) && isArrayType(target)) { let sourceBase = getBaseTypes((source as TypeReference).target as InterfaceType)[0]; - if (length((target as TypeReference).resolvedTypeArguments) === 2) { + if (length(target.resolvedTypeArguments) === 2) { sourceBase = getTypeWithThisArgument(sourceBase, ((source as TypeReference).target as InterfaceType).thisType); } return isRelatedTo(sourceBase, target, reportErrors); @@ -18229,7 +18229,7 @@ namespace ts { if (isArrayType(source) && isNonAugmentingArraySubtype(target)) { let targetBase = getBaseTypes((target as TypeReference).target as InterfaceType)[0]; - if (length((source as TypeReference).resolvedTypeArguments) === 2) { + if (length(source.resolvedTypeArguments) === 2) { targetBase = getTypeWithThisArgument(targetBase, ((target as TypeReference).target as InterfaceType).thisType); } return isRelatedTo(source, targetBase, reportErrors); From 15796c943c359c0abb4d4c3dcda26c44b3e003bc Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 14 Apr 2021 13:48:58 -0700 Subject: [PATCH 3/6] Generalize performance enhancement --- src/compiler/checker.ts | 29 +++++++++---------- ...tWithObjectMembersAccessibility.errors.txt | 24 +++++++-------- .../checkJsxChildrenProperty5.errors.txt | 6 ++-- .../checkJsxChildrenProperty7.errors.txt | 2 ++ .../classImplementsClass4.errors.txt | 6 ++-- .../classImplementsClass5.errors.txt | 12 +++++--- ...ertyIsRelatableToTargetProperty.errors.txt | 6 ++-- ...aceExtendingClassWithProtecteds.errors.txt | 6 ++-- .../reference/inheritance1.errors.txt | 19 +++++++----- ...andParentPrivateMemberCollision.errors.txt | 6 ++-- ...MemberCollisionWithPublicMember.errors.txt | 6 ++-- ...emberCollisionWithPrivateMember.errors.txt | 6 ++-- .../interfaceImplementation8.errors.txt | 12 +++++--- .../reference/privateNamesUnique-4.errors.txt | 6 ++-- .../reference/privateNamesUnique-5.errors.txt | 6 ++-- .../recursiveComplicatedClasses.types | 2 +- ...elessFunctionComponentOverload4.errors.txt | 2 ++ 17 files changed, 94 insertions(+), 62 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 20373d05d0671..5b949b8b6823d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18219,18 +18219,18 @@ namespace ts { return result; } - if (isNonAugmentingArraySubtype(source) && isArrayType(target)) { - let sourceBase = getBaseTypes((source as TypeReference).target as InterfaceType)[0]; - if (length(target.resolvedTypeArguments) === 2) { - sourceBase = getTypeWithThisArgument(sourceBase, ((source as TypeReference).target as InterfaceType).thisType); + let sourceBase = getSingleBaseForNonAugmentingSubtype(source); + if (sourceBase) { + if (getObjectFlags(target) & ObjectFlags.Reference && length(getTypeArguments((target as TypeReference))) > length((target as TypeReference).target.typeParameters)) { + sourceBase = getTypeWithThisArgument(sourceBase, last(getTypeArguments(source as TypeReference))); } return isRelatedTo(sourceBase, target, reportErrors); } - if (isArrayType(source) && isNonAugmentingArraySubtype(target)) { - let targetBase = getBaseTypes((target as TypeReference).target as InterfaceType)[0]; - if (length(source.resolvedTypeArguments) === 2) { - targetBase = getTypeWithThisArgument(targetBase, ((target as TypeReference).target as InterfaceType).thisType); + let targetBase = getSingleBaseForNonAugmentingSubtype(target); + if (targetBase) { + if (getObjectFlags(source) & ObjectFlags.Reference && length(getTypeArguments((source as TypeReference))) > length((source as TypeReference).target.typeParameters)) { + targetBase = getTypeWithThisArgument(targetBase, last(getTypeArguments(target as TypeReference))); } return isRelatedTo(source, targetBase, reportErrors); } @@ -19917,22 +19917,19 @@ namespace ts { return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } - function isNonAugmentingArraySubtype(type: Type) { + function getSingleBaseForNonAugmentingSubtype(type: Type) { if (!(getObjectFlags(type) & ObjectFlags.Reference) || !(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface)) { - return false; + return undefined; } const target = (type as TypeReference).target as InterfaceType; const bases = getBaseTypes(target); if (bases.length !== 1) { - return false; - } - if (!isArrayType(bases[0])) { - return false; + return undefined; } if (getMembersOfSymbol(type.symbol).size) { - return false; // If the interface has any members, they may subtype members in the base, so we should do a full structural comparison + return undefined; // If the interface has any members, they may subtype members in the base, so we should do a full structural comparison } - return true; + return !length(target.typeParameters) ? bases[0] : instantiateType(bases[0], createTypeMapper(target.typeParameters!, getTypeArguments(type as TypeReference).slice(0, target.typeParameters!.length))); } function isEmptyArrayLiteralType(type: Type): boolean { diff --git a/tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt b/tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt index c1093ab0ffe87..93774cefaa52c 100644 --- a/tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt +++ b/tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt @@ -17,7 +17,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(81,5): error TS2322: Type 'Base' is not assignable to type '{ foo: string; }'. Property 'foo' is private in type 'Base' but not in type '{ foo: string; }'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(82,5): error TS2322: Type 'I' is not assignable to type '{ foo: string; }'. - Property 'foo' is private in type 'I' but not in type '{ foo: string; }'. + Type 'Base' is not assignable to type '{ foo: string; }'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(84,5): error TS2322: Type 'E' is not assignable to type '{ foo: string; }'. Property 'foo' is private in type 'E' but not in type '{ foo: string; }'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(86,5): error TS2322: Type '{ foo: string; }' is not assignable to type 'Base'. @@ -27,15 +27,15 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(89,5): error TS2322: Type 'E' is not assignable to type 'Base'. Types have separate declarations of a private property 'foo'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(92,5): error TS2322: Type '{ foo: string; }' is not assignable to type 'I'. - Property 'foo' is private in type 'I' but not in type '{ foo: string; }'. + Type '{ foo: string; }' is not assignable to type 'Base'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(94,5): error TS2322: Type 'D' is not assignable to type 'I'. - Property 'foo' is private in type 'I' but not in type 'D'. + Type 'D' is not assignable to type 'Base'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(95,5): error TS2322: Type 'E' is not assignable to type 'I'. - Types have separate declarations of a private property 'foo'. + Type 'E' is not assignable to type 'Base'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(99,5): error TS2322: Type 'Base' is not assignable to type 'D'. Property 'foo' is private in type 'Base' but not in type 'D'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(100,5): error TS2322: Type 'I' is not assignable to type 'D'. - Property 'foo' is private in type 'I' but not in type 'D'. + Type 'Base' is not assignable to type 'D'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(101,5): error TS2322: Type 'E' is not assignable to type 'D'. Property 'foo' is private in type 'E' but not in type 'D'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(103,5): error TS2322: Type '{ foo: string; }' is not assignable to type 'E'. @@ -43,7 +43,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(104,5): error TS2322: Type 'Base' is not assignable to type 'E'. Types have separate declarations of a private property 'foo'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(105,5): error TS2322: Type 'I' is not assignable to type 'E'. - Types have separate declarations of a private property 'foo'. + Type 'Base' is not assignable to type 'E'. tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersAccessibility.ts(106,5): error TS2322: Type 'D' is not assignable to type 'E'. Property 'foo' is private in type 'E' but not in type 'D'. @@ -160,7 +160,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme a = i; // error ~ !!! error TS2322: Type 'I' is not assignable to type '{ foo: string; }'. -!!! error TS2322: Property 'foo' is private in type 'I' but not in type '{ foo: string; }'. +!!! error TS2322: Type 'Base' is not assignable to type '{ foo: string; }'. a = d; a = e; // error ~ @@ -185,16 +185,16 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme i = a; // error ~ !!! error TS2322: Type '{ foo: string; }' is not assignable to type 'I'. -!!! error TS2322: Property 'foo' is private in type 'I' but not in type '{ foo: string; }'. +!!! error TS2322: Type '{ foo: string; }' is not assignable to type 'Base'. i = b; i = d; // error ~ !!! error TS2322: Type 'D' is not assignable to type 'I'. -!!! error TS2322: Property 'foo' is private in type 'I' but not in type 'D'. +!!! error TS2322: Type 'D' is not assignable to type 'Base'. i = e; // error ~ !!! error TS2322: Type 'E' is not assignable to type 'I'. -!!! error TS2322: Types have separate declarations of a private property 'foo'. +!!! error TS2322: Type 'E' is not assignable to type 'Base'. i = i; d = a; @@ -205,7 +205,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme d = i; // error ~ !!! error TS2322: Type 'I' is not assignable to type 'D'. -!!! error TS2322: Property 'foo' is private in type 'I' but not in type 'D'. +!!! error TS2322: Type 'Base' is not assignable to type 'D'. d = e; // error ~ !!! error TS2322: Type 'E' is not assignable to type 'D'. @@ -222,7 +222,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme e = i; // errror ~ !!! error TS2322: Type 'I' is not assignable to type 'E'. -!!! error TS2322: Types have separate declarations of a private property 'foo'. +!!! error TS2322: Type 'Base' is not assignable to type 'E'. e = d; // errror ~ !!! error TS2322: Type 'D' is not assignable to type 'E'. diff --git a/tests/baselines/reference/checkJsxChildrenProperty5.errors.txt b/tests/baselines/reference/checkJsxChildrenProperty5.errors.txt index 476644ee33603..7f351d9aacd43 100644 --- a/tests/baselines/reference/checkJsxChildrenProperty5.errors.txt +++ b/tests/baselines/reference/checkJsxChildrenProperty5.errors.txt @@ -1,5 +1,6 @@ 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'. -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. +tests/cases/conformance/jsx/file.tsx(25,9): error TS2322: Type 'Element' is not assignable to type 'Button'. + Type 'ReactElement' is missing the following properties from type 'Button': render, setState, forceUpdate, state, and 2 more. 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. @@ -33,7 +34,8 @@ tests/cases/conformance/jsx/file.tsx(29,10): error TS2740: Type 'typeof Button'