diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8547fc7e1760e..0bfbb5021abe0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8885,6 +8885,16 @@ namespace ts { return false; } + function reportElaboratedRelationError(headMessage: DiagnosticMessage | undefined, source: Type, target: Type) { + if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) { + tryElaborateErrorsForPrimitivesAndObjects(source, target); + } + else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) { + reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead); + } + reportRelationError(headMessage, source, target); + } + /** * Compare two types and return * * Ternary.True if they are related with no assumptions, @@ -8899,20 +8909,36 @@ namespace ts { target = (target).regularType; } // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases - if (source === target) return Ternary.True; + if (source === target || relation !== identityRelation && isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) { + return Ternary.True; + } - if (relation === identityRelation) { - return isIdenticalTo(source, target); + const id = getRelationKey(source, target, relation); + const related = relation.get(id); + if (related !== undefined) { + // If we need to report errors, and the result is RelationComparisonResult.Failed, then we need + // to redo our work to generate an error message. Otherwise, we can just return the cached result. + if (!reportErrors || related !== RelationComparisonResult.Failed) { + if (reportErrors && related !== RelationComparisonResult.Succeeded) { + reportElaboratedRelationError(headMessage, source, target); + } + return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; + } + if (reportErrors && related === RelationComparisonResult.Failed) { + relation.set(id, RelationComparisonResult.FailedAndReported); + } } - if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; + if (relation === identityRelation) { + return cacheResult(isIdenticalTo(source, target, id), id, /*reportErrors*/ false); + } if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) { if (hasExcessProperties(source, target, reportErrors)) { if (reportErrors) { reportRelationError(headMessage, source, target); } - return Ternary.False; + return cacheResult(Ternary.False, id, reportErrors); } // Above we check for excess properties with respect to the entire target type. When union // and intersection types are further deconstructed on the target side, we don't want to @@ -8944,6 +8970,7 @@ namespace ts { reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); } } + // Intentionally not cached, so common property errors are repeated return Ternary.False; } @@ -8985,7 +9012,7 @@ namespace ts { result = someTypeRelatedToType(source, target, /*reportErrors*/ false); } if (!result && (source.flags & TypeFlags.StructuredOrTypeVariable || target.flags & TypeFlags.StructuredOrTypeVariable)) { - if (result = recursiveTypeRelatedTo(source, target, reportErrors)) { + if (result = recursiveTypeRelatedTo(source, target, id, reportErrors)) { errorInfo = saveErrorInfo; } } @@ -8994,21 +9021,23 @@ namespace ts { isIntersectionConstituent = saveIsIntersectionConstituent; if (!result && reportErrors) { - if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) { - tryElaborateErrorsForPrimitivesAndObjects(source, target); - } - else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) { - reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead); - } - reportRelationError(headMessage, source, target); + reportElaboratedRelationError(headMessage, source, target); + } + + return cacheResult(result, id, reportErrors); + } + + function cacheResult(result: Ternary, comparisonId: string, reportErrors?: boolean) { + if (!relation.has(comparisonId) && result !== Ternary.Maybe) { + relation.set(comparisonId, result ? RelationComparisonResult.Succeeded : reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed); } return result; } - function isIdenticalTo(source: Type, target: Type): Ternary { + function isIdenticalTo(source: Type, target: Type, id: string): Ternary { let result: Ternary; if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { - return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false); + return recursiveTypeRelatedTo(source, target, id, /*reportErrors*/ false); } if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union || source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { @@ -9184,22 +9213,10 @@ namespace ts { // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are // equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion // and issue an error. Otherwise, actually compare the structure of the two types. - function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { + function recursiveTypeRelatedTo(source: Type, target: Type, id: string, reportErrors: boolean): Ternary { if (overflow) { return Ternary.False; } - const id = getRelationKey(source, target, relation); - const related = relation.get(id); - if (related !== undefined) { - if (reportErrors && related === RelationComparisonResult.Failed) { - // We are elaborating errors and the cached result is an unreported failure. Record the result as a reported - // failure and continue computing the relation such that errors get reported. - relation.set(id, RelationComparisonResult.FailedAndReported); - } - else { - return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; - } - } if (!maybeKeys) { maybeKeys = []; sourceStack = []; @@ -9239,9 +9256,6 @@ namespace ts { } } else { - // A false result goes straight into global cache (when something is false under - // assumptions it will also be false without assumptions) - relation.set(id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed); maybeCount = maybeStart; } return result; diff --git a/tests/baselines/reference/arityAndOrderCompatibility01.errors.txt b/tests/baselines/reference/arityAndOrderCompatibility01.errors.txt index 5a367a4122fe0..970372ca0c1c6 100644 --- a/tests/baselines/reference/arityAndOrderCompatibility01.errors.txt +++ b/tests/baselines/reference/arityAndOrderCompatibility01.errors.txt @@ -31,7 +31,6 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(26,5): error Types of property 'pop' are incompatible. Type '() => string | number' is not assignable to type '() => string'. Type 'string | number' is not assignable to type 'string'. - Type 'number' is not assignable to type 'string'. tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(27,5): error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[string]'. Property 'length' is missing in type '{ 0: string; 1: number; }'. tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(28,5): error TS2322: Type '[string, number]' is not assignable to type '[number, string]'. @@ -118,7 +117,6 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(30,5): error !!! error TS2322: Types of property 'pop' are incompatible. !!! error TS2322: Type '() => string | number' is not assignable to type '() => string'. !!! error TS2322: Type 'string | number' is not assignable to type 'string'. -!!! error TS2322: Type 'number' is not assignable to type 'string'. var m3: [string] = z; ~~ !!! error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[string]'. diff --git a/tests/baselines/reference/callWithSpread2.errors.txt b/tests/baselines/reference/callWithSpread2.errors.txt index 89617ab6074c3..44bb2bb8f2f19 100644 --- a/tests/baselines/reference/callWithSpread2.errors.txt +++ b/tests/baselines/reference/callWithSpread2.errors.txt @@ -1,15 +1,10 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(30,5): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. Type 'string' is not assignable to type 'number'. tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(31,5): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. - Type 'string' is not assignable to type 'number'. tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(32,13): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. - Type 'string' is not assignable to type 'number'. tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(33,13): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. - Type 'string' is not assignable to type 'number'. tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(34,11): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. - Type 'string' is not assignable to type 'number'. tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(35,11): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. - Type 'string' is not assignable to type 'number'. tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(36,1): error TS2556: Expected 1-3 arguments, but got a minimum of 0. tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(37,1): error TS2556: Expected 1-3 arguments, but got a minimum of 0. tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(38,1): error TS2556: Expected 1-3 arguments, but got a minimum of 0. @@ -52,23 +47,18 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread2.ts(38,1): erro all(...tuple) ~~~~~~~~ !!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. -!!! error TS2345: Type 'string' is not assignable to type 'number'. prefix("b", ...mixed) ~~~~~~~~ !!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. -!!! error TS2345: Type 'string' is not assignable to type 'number'. prefix("c", ...tuple) ~~~~~~~~ !!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. -!!! error TS2345: Type 'string' is not assignable to type 'number'. rest("e", ...mixed) ~~~~~~~~ !!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. -!!! error TS2345: Type 'string' is not assignable to type 'number'. rest("f", ...tuple) ~~~~~~~~ !!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. -!!! error TS2345: Type 'string' is not assignable to type 'number'. prefix(...ns) // required parameters are required ~~~~~~~~~~~~~ !!! error TS2556: Expected 1-3 arguments, but got a minimum of 0. diff --git a/tests/baselines/reference/checkJsxChildrenProperty7.errors.txt b/tests/baselines/reference/checkJsxChildrenProperty7.errors.txt index ea31d008b2f54..5354d1fbb803c 100644 --- a/tests/baselines/reference/checkJsxChildrenProperty7.errors.txt +++ b/tests/baselines/reference/checkJsxChildrenProperty7.errors.txt @@ -9,12 +9,10 @@ tests/cases/conformance/jsx/file.tsx(25,16): error TS2322: Type '{ a: 10; b: "hi Type '{ a: 10; b: "hi"; children: (string | Element)[]; }' is not assignable to type 'Prop'. Types of property 'children' are incompatible. Type '(string | Element)[]' is not assignable to type 'Element | Element[]'. - Type '(string | Element)[]' is not assignable to type 'Element[]'. tests/cases/conformance/jsx/file.tsx(27,16): error TS2322: Type '{ a: 10; b: "hi"; children: (string | Element)[]; }' is not assignable to type 'IntrinsicAttributes & Prop'. Type '{ a: 10; b: "hi"; children: (string | Element)[]; }' is not assignable to type 'Prop'. Types of property 'children' are incompatible. Type '(string | Element)[]' is not assignable to type 'Element | Element[]'. - Type '(string | Element)[]' is not assignable to type 'Element[]'. ==== tests/cases/conformance/jsx/file.tsx (3 errors) ==== @@ -56,7 +54,6 @@ tests/cases/conformance/jsx/file.tsx(27,16): error TS2322: Type '{ a: 10; b: "hi !!! error TS2322: Type '{ a: 10; b: "hi"; children: (string | Element)[]; }' is not assignable to type 'Prop'. !!! error TS2322: Types of property 'children' are incompatible. !!! error TS2322: Type '(string | Element)[]' is not assignable to type 'Element | Element[]'. -!!! error TS2322: Type '(string | Element)[]' is not assignable to type 'Element[]'. ; let k3 =