diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3cc7ff57f8cba..d9b54acc81f6b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17195,12 +17195,13 @@ namespace ts { function getNormalizedType(type: Type, writing: boolean): Type { while (true) { - const t = isFreshLiteralType(type) ? (type).regularType : + let t = isFreshLiteralType(type) ? (type).regularType : getObjectFlags(type) & ObjectFlags.Reference && (type).node ? createTypeReference((type).target, getTypeArguments(type)) : type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) : type.flags & TypeFlags.Substitution ? writing ? (type).baseType : (type).substitute : type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : type; + t = getSingleBaseForNonAugmentingSubtype(t) || t; if (t === type) break; type = t; } @@ -17702,8 +17703,10 @@ namespace ts { function reportErrorResults(source: Type, target: Type, result: Ternary, isComparingJsxAttributes: boolean) { if (!result && reportErrors) { - source = originalSource.aliasSymbol ? originalSource : source; - target = originalTarget.aliasSymbol ? originalTarget : target; + const sourceHasBase = !!getSingleBaseForNonAugmentingSubtype(originalSource); + const targetHasBase = !!getSingleBaseForNonAugmentingSubtype(originalTarget); + source = (originalSource.aliasSymbol || sourceHasBase) ? originalSource : source; + target = (originalTarget.aliasSymbol || targetHasBase) ? originalTarget : target; let maybeSuppress = overrideNextErrorInfo > 0; if (maybeSuppress) { overrideNextErrorInfo--; @@ -19956,6 +19959,30 @@ namespace ts { return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } + function getSingleBaseForNonAugmentingSubtype(type: Type) { + if (!(getObjectFlags(type) & ObjectFlags.Reference) || !(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface)) { + return undefined; + } + if (getObjectFlags(type) & ObjectFlags.IdenticalBaseTypeCalculated) { + return getObjectFlags(type) & ObjectFlags.IdenticalBaseTypeExists ? (type as TypeReference).cachedEquivalentBaseType : undefined; + } + (type as TypeReference).objectFlags |= ObjectFlags.IdenticalBaseTypeCalculated; + const target = (type as TypeReference).target as InterfaceType; + const bases = getBaseTypes(target); + if (bases.length !== 1) { + return undefined; + } + if (getMembersOfSymbol(type.symbol).size) { + return undefined; // If the interface has any members, they may subtype members in the base, so we should do a full structural comparison + } + let instantiatedBase = !length(target.typeParameters) ? bases[0] : instantiateType(bases[0], createTypeMapper(target.typeParameters!, getTypeArguments(type as TypeReference).slice(0, target.typeParameters!.length))); + if (length(getTypeArguments(type as TypeReference)) > length(target.typeParameters)) { + instantiatedBase = getTypeWithThisArgument(instantiatedBase, last(getTypeArguments(type as TypeReference))); + } + (type as TypeReference).objectFlags |= ObjectFlags.IdenticalBaseTypeExists; + return (type as TypeReference).cachedEquivalentBaseType = instantiatedBase; + } + function isEmptyArrayLiteralType(type: Type): boolean { const elementType = getElementTypeOfArrayType(type); return strictNullChecks ? elementType === implicitNeverType : elementType === undefinedWideningType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0dda734aedf85..4bded5742c59a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5200,6 +5200,11 @@ namespace ts { ObjectRestType = 1 << 23, // Originates in object rest declaration /* @internal */ IsClassInstanceClone = 1 << 24, // Type is a clone of a class instance type + // Flags that require TypeFlags.Object and ObjectFlags.Reference + /* @internal */ + IdenticalBaseTypeCalculated = 1 << 25, // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already + /* @internal */ + IdenticalBaseTypeExists = 1 << 26, // has a defined cachedEquivalentBaseType member // Flags that require TypeFlags.UnionOrIntersection /* @internal */ @@ -5281,6 +5286,8 @@ namespace ts { resolvedTypeArguments?: readonly Type[]; // Resolved type reference type arguments /* @internal */ literalType?: TypeReference; // Clone of type with ObjectFlags.ArrayLiteral set + /* @internal */ + cachedEquivalentBaseType?: Type; // Only set on references to class or interfaces with a single base type and no augmentations } export interface DeferredTypeReference extends TypeReference { diff --git a/tests/baselines/reference/arrayLiterals3.errors.txt b/tests/baselines/reference/arrayLiterals3.errors.txt index 8ea3d1583b6fb..3b2d25d17f4ec 100644 --- a/tests/baselines/reference/arrayLiterals3.errors.txt +++ b/tests/baselines/reference/arrayLiterals3.errors.txt @@ -8,9 +8,8 @@ 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' is not assignable to type 'Number'. + Type 'string | number' is not assignable to type 'Number'. + Type 'string' is not assignable to type 'Number'. ==== tests/cases/conformance/expressions/arrayLiterals/arrayLiterals3.ts (7 errors) ==== @@ -65,7 +64,6 @@ 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' 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 diff --git a/tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt b/tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt index c1093ab0ffe87..6ca6b5e21e0ea 100644 --- a/tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt +++ b/tests/baselines/reference/assignmentCompatWithObjectMembersAccessibility.errors.txt @@ -17,7 +17,6 @@ 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; }'. 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 +26,11 @@ 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; }'. 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'. 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'. 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'. 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 +38,6 @@ 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'. 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 +154,6 @@ 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; }'. a = d; a = e; // error ~ @@ -185,16 +178,13 @@ 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; }'. 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'. i = e; // error ~ !!! error TS2322: Type 'E' is not assignable to type 'I'. -!!! error TS2322: Types have separate declarations of a private property 'foo'. i = i; d = a; @@ -205,7 +195,6 @@ 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'. d = e; // error ~ !!! error TS2322: Type 'E' is not assignable to type 'D'. @@ -222,7 +211,6 @@ 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'. 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..aad013f4d8544 100644 --- a/tests/baselines/reference/checkJsxChildrenProperty5.errors.txt +++ b/tests/baselines/reference/checkJsxChildrenProperty5.errors.txt @@ -1,5 +1,5 @@ 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 TS2740: 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 +33,7 @@ tests/cases/conformance/jsx/file.tsx(29,10): error TS2740: Type 'typeof Button'