diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 48bc0da113816..c9180623e64f0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23190,29 +23190,35 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be) related = relation === identityRelation ? isRelatedTo(s, t, RecursionFlags.Both, /*reportErrors*/ false) : compareTypesIdentical(s, t); } - else if (variance === VarianceFlags.Covariant) { - related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); - } - else if (variance === VarianceFlags.Contravariant) { - related = isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); - } - else if (variance === VarianceFlags.Bivariant) { - // In the bivariant case we first compare contravariantly without reporting - // errors. Then, if that doesn't succeed, we compare covariantly with error - // reporting. Thus, error elaboration will be based on the the covariant check, - // which is generally easier to reason about. - related = isRelatedTo(t, s, RecursionFlags.Both, /*reportErrors*/ false); - if (!related) { + else { + // Propagate unreliable variance flag + if (inVarianceComputation && varianceFlags & VarianceFlags.Unreliable) { + instantiateType(s, reportUnreliableMapper); + } + if (variance === VarianceFlags.Covariant) { related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); } - } - else { - // In the invariant case we first compare covariantly, and only when that - // succeeds do we proceed to compare contravariantly. Thus, error elaboration - // will typically be based on the covariant check. - related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); - if (related) { - related &= isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); + else if (variance === VarianceFlags.Contravariant) { + related = isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); + } + else if (variance === VarianceFlags.Bivariant) { + // In the bivariant case we first compare contravariantly without reporting + // errors. Then, if that doesn't succeed, we compare covariantly with error + // reporting. Thus, error elaboration will be based on the the covariant check, + // which is generally easier to reason about. + related = isRelatedTo(t, s, RecursionFlags.Both, /*reportErrors*/ false); + if (!related) { + related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); + } + } + else { + // In the invariant case we first compare covariantly, and only when that + // succeeds do we proceed to compare contravariantly. Thus, error elaboration + // will typically be based on the covariant check. + related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); + if (related) { + related &= isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); + } } } if (!related) { diff --git a/tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types b/tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types index 765ca3234d8cb..29b958c768ad3 100644 --- a/tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types +++ b/tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types @@ -1,5 +1,8 @@ //// [tests/cases/compiler/circularlySimplifyingConditionalTypesNoCrash.ts] //// +=== Performance Stats === +Instantiation count: 1,000 + === circularlySimplifyingConditionalTypesNoCrash.ts === type Omit = Pick>; >Omit : Omit diff --git a/tests/baselines/reference/variancePropagation.symbols b/tests/baselines/reference/variancePropagation.symbols new file mode 100644 index 0000000000000..1c138b4bcffdf --- /dev/null +++ b/tests/baselines/reference/variancePropagation.symbols @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/variancePropagation.ts] //// + +=== variancePropagation.ts === +// https://github.com/microsoft/TypeScript/issues/62606 + +interface DerivedTable { +>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0)) +>S : Symbol(S, Decl(variancePropagation.ts, 2, 23)) +>base : Symbol(base, Decl(variancePropagation.ts, 2, 34)) +>new : Symbol(new, Decl(variancePropagation.ts, 2, 45)) + + // Error disappears when these property declarations are reversed + schema: S["base"] & S["new"] +>schema : Symbol(DerivedTable.schema, Decl(variancePropagation.ts, 2, 59)) +>S : Symbol(S, Decl(variancePropagation.ts, 2, 23)) +>S : Symbol(S, Decl(variancePropagation.ts, 2, 23)) + + readonlySchema: Readonly +>readonlySchema : Symbol(DerivedTable.readonlySchema, Decl(variancePropagation.ts, 4, 32)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>S : Symbol(S, Decl(variancePropagation.ts, 2, 23)) +>S : Symbol(S, Decl(variancePropagation.ts, 2, 23)) +} + +interface Base { baseProp: number; } +>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1)) +>baseProp : Symbol(Base.baseProp, Decl(variancePropagation.ts, 8, 16)) + +interface New { newProp: boolean; } +>New : Symbol(New, Decl(variancePropagation.ts, 8, 36)) +>newProp : Symbol(New.newProp, Decl(variancePropagation.ts, 9, 16)) + +declare const source: DerivedTable<{ base: Base, new: New }> +>source : Symbol(source, Decl(variancePropagation.ts, 11, 13)) +>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0)) +>base : Symbol(base, Decl(variancePropagation.ts, 11, 36)) +>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1)) +>new : Symbol(new, Decl(variancePropagation.ts, 11, 48)) +>New : Symbol(New, Decl(variancePropagation.ts, 8, 36)) + +const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error +>destination : Symbol(destination, Decl(variancePropagation.ts, 12, 5)) +>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0)) +>base : Symbol(base, Decl(variancePropagation.ts, 12, 33)) +>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1)) +>new : Symbol(new, Decl(variancePropagation.ts, 12, 45)) +>New : Symbol(New, Decl(variancePropagation.ts, 8, 36)) +>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1)) +>source : Symbol(source, Decl(variancePropagation.ts, 11, 13)) + diff --git a/tests/baselines/reference/variancePropagation.types b/tests/baselines/reference/variancePropagation.types new file mode 100644 index 0000000000000..c3d93d52b891d --- /dev/null +++ b/tests/baselines/reference/variancePropagation.types @@ -0,0 +1,45 @@ +//// [tests/cases/compiler/variancePropagation.ts] //// + +=== variancePropagation.ts === +// https://github.com/microsoft/TypeScript/issues/62606 + +interface DerivedTable { +>base : any +>new : any + + // Error disappears when these property declarations are reversed + schema: S["base"] & S["new"] +>schema : S["base"] & S["new"] +> : ^^^^^^^^^^^^^^^^^^^^ + + readonlySchema: Readonly +>readonlySchema : Readonly +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} + +interface Base { baseProp: number; } +>baseProp : number +> : ^^^^^^ + +interface New { newProp: boolean; } +>newProp : boolean +> : ^^^^^^^ + +declare const source: DerivedTable<{ base: Base, new: New }> +>source : DerivedTable<{ base: Base; new: New; }> +> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^ +>base : Base +> : ^^^^ +>new : New +> : ^^^ + +const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error +>destination : DerivedTable<{ base: Base; new: New & Base; }> +> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^ +>base : Base +> : ^^^^ +>new : New & Base +> : ^^^^^^^^^^ +>source : DerivedTable<{ base: Base; new: New; }> +> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^ + diff --git a/tests/cases/compiler/variancePropagation.ts b/tests/cases/compiler/variancePropagation.ts new file mode 100644 index 0000000000000..ab62d64928003 --- /dev/null +++ b/tests/cases/compiler/variancePropagation.ts @@ -0,0 +1,16 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/62606 + +interface DerivedTable { + // Error disappears when these property declarations are reversed + schema: S["base"] & S["new"] + readonlySchema: Readonly +} + +interface Base { baseProp: number; } +interface New { newProp: boolean; } + +declare const source: DerivedTable<{ base: Base, new: New }> +const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error