diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bde22f07b35..05166d56888 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19368,13 +19368,21 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") } function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary { + const saveErrorInfo = captureErrorCalculationState(); + const result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState, saveErrorInfo); + if (result) { + resetErrorInfo(saveErrorInfo); + } + return result; + } + + function structuredTypeRelatedToWorker(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState, saveErrorInfo: ReturnType): Ternary { if (intersectionState & IntersectionState.PropertyCheck) { return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None); } let result: Ternary; let originalErrorInfo: DiagnosticMessageChain | undefined; let varianceCheckFailed = false; - const saveErrorInfo = captureErrorCalculationState(); let sourceFlags = source.flags; const targetFlags = target.flags; if (relation === identityRelation) { @@ -19438,7 +19446,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") if (constraint && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19572,7 +19579,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") result &= isRelatedTo((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType, RecursionFlags.Both, reportErrors); } if (result) { - resetErrorInfo(saveErrorInfo); return result; } if (reportErrors) { @@ -19677,7 +19683,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") // If we reach 10 levels of nesting for the same conditional type, assume it is an infinitely expanding recursive // conditional type and bail out with a Ternary.Maybe result. if (isDeeplyNestedType(target, targetStack, targetDepth, 10)) { - resetErrorInfo(saveErrorInfo); return Ternary.Maybe; } const c = target as ConditionalType; @@ -19692,7 +19697,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") if (result = skipTrue ? Ternary.True : isRelatedTo(source, getTrueTypeFromConditionalType(c), RecursionFlags.Target, /*reportErrors*/ false)) { result &= skipFalse ? Ternary.True : isRelatedTo(source, getFalseTypeFromConditionalType(c), RecursionFlags.Target, /*reportErrors*/ false); if (result) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19725,12 +19729,10 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") const constraint = getConstraintOfType(source as TypeVariable) || unknownType; // hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) { - resetErrorInfo(saveErrorInfo); return result; } // slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, RecursionFlags.Source, reportErrors && constraint !== unknownType && !(targetFlags & sourceFlags & TypeFlags.TypeParameter), /*headMessage*/ undefined, intersectionState)) { - resetErrorInfo(saveErrorInfo); return result; } if (isMappedTypeGenericIndexedAccess(source)) { @@ -19739,7 +19741,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") const indexConstraint = getConstraintOfType((source as IndexedAccessType).indexType); if (indexConstraint) { if (result = isRelatedTo(getIndexedAccessType((source as IndexedAccessType).objectType, indexConstraint), target, RecursionFlags.Source, reportErrors)) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19748,7 +19749,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") } else if (sourceFlags & TypeFlags.Index) { if (result = isRelatedTo(keyofConstraintType, target, RecursionFlags.Source, reportErrors)) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19756,7 +19756,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") if (!(targetFlags & TypeFlags.TemplateLiteral)) { const constraint = getBaseConstraintOfType(source); if (constraint && constraint !== source && (result = isRelatedTo(constraint, target, RecursionFlags.Source, reportErrors))) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19767,14 +19766,12 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") return Ternary.False; } if (result = isRelatedTo((source as StringMappingType).type, (target as StringMappingType).type, RecursionFlags.Both, reportErrors)) { - resetErrorInfo(saveErrorInfo); return result; } } else { const constraint = getBaseConstraintOfType(source); if (constraint && (result = isRelatedTo(constraint, target, RecursionFlags.Source, reportErrors))) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19783,7 +19780,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") // If we reach 10 levels of nesting for the same conditional type, assume it is an infinitely expanding recursive // conditional type and bail out with a Ternary.Maybe result. if (isDeeplyNestedType(source, sourceStack, sourceDepth, 10)) { - resetErrorInfo(saveErrorInfo); return Ternary.Maybe; } if (targetFlags & TypeFlags.Conditional) { @@ -19806,7 +19802,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") result &= isRelatedTo(getFalseTypeFromConditionalType(source as ConditionalType), getFalseTypeFromConditionalType(target as ConditionalType), RecursionFlags.Both, reportErrors); } if (result) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19817,7 +19812,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") const distributiveConstraint = hasNonCircularBaseConstraint(source) ? getConstraintOfDistributiveConditionalType(source as ConditionalType) : undefined; if (distributiveConstraint) { if (result = isRelatedTo(distributiveConstraint, target, RecursionFlags.Source, reportErrors)) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19828,7 +19822,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") const defaultConstraint = getDefaultConstraintOfConditionalType(source as ConditionalType); if (defaultConstraint) { if (result = isRelatedTo(defaultConstraint, target, RecursionFlags.Source, reportErrors)) { - resetErrorInfo(saveErrorInfo); return result; } } @@ -19841,7 +19834,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") if (isGenericMappedType(target)) { if (isGenericMappedType(source)) { if (result = mappedTypeRelatedTo(source, target, reportErrors)) { - resetErrorInfo(saveErrorInfo); return result; } } diff --git a/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.js b/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.js new file mode 100644 index 00000000000..1cc939eacbf --- /dev/null +++ b/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.js @@ -0,0 +1,48 @@ +//// [relatedViaDiscriminatedTypeNoError.ts] +class Model { + constructor(public flag: boolean) {} +} + +type DiscriminatedUnion = { flag: true } | { flag: false }; +class A { + constructor(public model: T) { } +} + +class B extends A { } + + +//// [relatedViaDiscriminatedTypeNoError.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var Model = /** @class */ (function () { + function Model(flag) { + this.flag = flag; + } + return Model; +}()); +var A = /** @class */ (function () { + function A(model) { + this.model = model; + } + return A; +}()); +var B = /** @class */ (function (_super) { + __extends(B, _super); + function B() { + return _super !== null && _super.apply(this, arguments) || this; + } + return B; +}(A)); diff --git a/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.symbols b/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.symbols new file mode 100644 index 00000000000..89b3396c284 --- /dev/null +++ b/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.symbols @@ -0,0 +1,28 @@ +=== tests/cases/compiler/relatedViaDiscriminatedTypeNoError.ts === +class Model { +>Model : Symbol(Model, Decl(relatedViaDiscriminatedTypeNoError.ts, 0, 0)) + + constructor(public flag: boolean) {} +>flag : Symbol(Model.flag, Decl(relatedViaDiscriminatedTypeNoError.ts, 1, 16)) +} + +type DiscriminatedUnion = { flag: true } | { flag: false }; +>DiscriminatedUnion : Symbol(DiscriminatedUnion, Decl(relatedViaDiscriminatedTypeNoError.ts, 2, 1)) +>flag : Symbol(flag, Decl(relatedViaDiscriminatedTypeNoError.ts, 4, 27)) +>flag : Symbol(flag, Decl(relatedViaDiscriminatedTypeNoError.ts, 4, 44)) + +class A { +>A : Symbol(A, Decl(relatedViaDiscriminatedTypeNoError.ts, 4, 59)) +>T : Symbol(T, Decl(relatedViaDiscriminatedTypeNoError.ts, 5, 8)) +>DiscriminatedUnion : Symbol(DiscriminatedUnion, Decl(relatedViaDiscriminatedTypeNoError.ts, 2, 1)) + + constructor(public model: T) { } +>model : Symbol(A.model, Decl(relatedViaDiscriminatedTypeNoError.ts, 6, 16)) +>T : Symbol(T, Decl(relatedViaDiscriminatedTypeNoError.ts, 5, 8)) +} + +class B extends A { } +>B : Symbol(B, Decl(relatedViaDiscriminatedTypeNoError.ts, 7, 1)) +>A : Symbol(A, Decl(relatedViaDiscriminatedTypeNoError.ts, 4, 59)) +>Model : Symbol(Model, Decl(relatedViaDiscriminatedTypeNoError.ts, 0, 0)) + diff --git a/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.types b/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.types new file mode 100644 index 00000000000..8c3f0a824fb --- /dev/null +++ b/tests/baselines/reference/relatedViaDiscriminatedTypeNoError.types @@ -0,0 +1,26 @@ +=== tests/cases/compiler/relatedViaDiscriminatedTypeNoError.ts === +class Model { +>Model : Model + + constructor(public flag: boolean) {} +>flag : boolean +} + +type DiscriminatedUnion = { flag: true } | { flag: false }; +>DiscriminatedUnion : { flag: true; } | { flag: false; } +>flag : true +>true : true +>flag : false +>false : false + +class A { +>A : A + + constructor(public model: T) { } +>model : T +} + +class B extends A { } +>B : B +>A : A + diff --git a/tests/cases/compiler/relatedViaDiscriminatedTypeNoError.ts b/tests/cases/compiler/relatedViaDiscriminatedTypeNoError.ts new file mode 100644 index 00000000000..4edd82e1b6c --- /dev/null +++ b/tests/cases/compiler/relatedViaDiscriminatedTypeNoError.ts @@ -0,0 +1,10 @@ +class Model { + constructor(public flag: boolean) {} +} + +type DiscriminatedUnion = { flag: true } | { flag: false }; +class A { + constructor(public model: T) { } +} + +class B extends A { }