Skip to content

Commit cc05d94

Browse files
authored
Propagate variance reliability (#62604)
1 parent 9222837 commit cc05d94

File tree

5 files changed

+141
-21
lines changed

5 files changed

+141
-21
lines changed

src/compiler/checker.ts

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23190,29 +23190,35 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2319023190
// the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be)
2319123191
related = relation === identityRelation ? isRelatedTo(s, t, RecursionFlags.Both, /*reportErrors*/ false) : compareTypesIdentical(s, t);
2319223192
}
23193-
else if (variance === VarianceFlags.Covariant) {
23194-
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
23195-
}
23196-
else if (variance === VarianceFlags.Contravariant) {
23197-
related = isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
23198-
}
23199-
else if (variance === VarianceFlags.Bivariant) {
23200-
// In the bivariant case we first compare contravariantly without reporting
23201-
// errors. Then, if that doesn't succeed, we compare covariantly with error
23202-
// reporting. Thus, error elaboration will be based on the the covariant check,
23203-
// which is generally easier to reason about.
23204-
related = isRelatedTo(t, s, RecursionFlags.Both, /*reportErrors*/ false);
23205-
if (!related) {
23193+
else {
23194+
// Propagate unreliable variance flag
23195+
if (inVarianceComputation && varianceFlags & VarianceFlags.Unreliable) {
23196+
instantiateType(s, reportUnreliableMapper);
23197+
}
23198+
if (variance === VarianceFlags.Covariant) {
2320623199
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
2320723200
}
23208-
}
23209-
else {
23210-
// In the invariant case we first compare covariantly, and only when that
23211-
// succeeds do we proceed to compare contravariantly. Thus, error elaboration
23212-
// will typically be based on the covariant check.
23213-
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
23214-
if (related) {
23215-
related &= isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
23201+
else if (variance === VarianceFlags.Contravariant) {
23202+
related = isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
23203+
}
23204+
else if (variance === VarianceFlags.Bivariant) {
23205+
// In the bivariant case we first compare contravariantly without reporting
23206+
// errors. Then, if that doesn't succeed, we compare covariantly with error
23207+
// reporting. Thus, error elaboration will be based on the the covariant check,
23208+
// which is generally easier to reason about.
23209+
related = isRelatedTo(t, s, RecursionFlags.Both, /*reportErrors*/ false);
23210+
if (!related) {
23211+
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
23212+
}
23213+
}
23214+
else {
23215+
// In the invariant case we first compare covariantly, and only when that
23216+
// succeeds do we proceed to compare contravariantly. Thus, error elaboration
23217+
// will typically be based on the covariant check.
23218+
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
23219+
if (related) {
23220+
related &= isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
23221+
}
2321623222
}
2321723223
}
2321823224
if (!related) {

tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//// [tests/cases/compiler/circularlySimplifyingConditionalTypesNoCrash.ts] ////
22

3+
=== Performance Stats ===
4+
Instantiation count: 1,000
5+
36
=== circularlySimplifyingConditionalTypesNoCrash.ts ===
47
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
58
>Omit : Omit<T, K>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [tests/cases/compiler/variancePropagation.ts] ////
2+
3+
=== variancePropagation.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/62606
5+
6+
interface DerivedTable<S extends { base: any; new: any }> {
7+
>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0))
8+
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
9+
>base : Symbol(base, Decl(variancePropagation.ts, 2, 34))
10+
>new : Symbol(new, Decl(variancePropagation.ts, 2, 45))
11+
12+
// Error disappears when these property declarations are reversed
13+
schema: S["base"] & S["new"]
14+
>schema : Symbol(DerivedTable.schema, Decl(variancePropagation.ts, 2, 59))
15+
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
16+
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
17+
18+
readonlySchema: Readonly<S["base"] & S["new"]>
19+
>readonlySchema : Symbol(DerivedTable.readonlySchema, Decl(variancePropagation.ts, 4, 32))
20+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
21+
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
22+
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
23+
}
24+
25+
interface Base { baseProp: number; }
26+
>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1))
27+
>baseProp : Symbol(Base.baseProp, Decl(variancePropagation.ts, 8, 16))
28+
29+
interface New { newProp: boolean; }
30+
>New : Symbol(New, Decl(variancePropagation.ts, 8, 36))
31+
>newProp : Symbol(New.newProp, Decl(variancePropagation.ts, 9, 16))
32+
33+
declare const source: DerivedTable<{ base: Base, new: New }>
34+
>source : Symbol(source, Decl(variancePropagation.ts, 11, 13))
35+
>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0))
36+
>base : Symbol(base, Decl(variancePropagation.ts, 11, 36))
37+
>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1))
38+
>new : Symbol(new, Decl(variancePropagation.ts, 11, 48))
39+
>New : Symbol(New, Decl(variancePropagation.ts, 8, 36))
40+
41+
const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error
42+
>destination : Symbol(destination, Decl(variancePropagation.ts, 12, 5))
43+
>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0))
44+
>base : Symbol(base, Decl(variancePropagation.ts, 12, 33))
45+
>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1))
46+
>new : Symbol(new, Decl(variancePropagation.ts, 12, 45))
47+
>New : Symbol(New, Decl(variancePropagation.ts, 8, 36))
48+
>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1))
49+
>source : Symbol(source, Decl(variancePropagation.ts, 11, 13))
50+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//// [tests/cases/compiler/variancePropagation.ts] ////
2+
3+
=== variancePropagation.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/62606
5+
6+
interface DerivedTable<S extends { base: any; new: any }> {
7+
>base : any
8+
>new : any
9+
10+
// Error disappears when these property declarations are reversed
11+
schema: S["base"] & S["new"]
12+
>schema : S["base"] & S["new"]
13+
> : ^^^^^^^^^^^^^^^^^^^^
14+
15+
readonlySchema: Readonly<S["base"] & S["new"]>
16+
>readonlySchema : Readonly<S["base"] & S["new"]>
17+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
}
19+
20+
interface Base { baseProp: number; }
21+
>baseProp : number
22+
> : ^^^^^^
23+
24+
interface New { newProp: boolean; }
25+
>newProp : boolean
26+
> : ^^^^^^^
27+
28+
declare const source: DerivedTable<{ base: Base, new: New }>
29+
>source : DerivedTable<{ base: Base; new: New; }>
30+
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^
31+
>base : Base
32+
> : ^^^^
33+
>new : New
34+
> : ^^^
35+
36+
const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error
37+
>destination : DerivedTable<{ base: Base; new: New & Base; }>
38+
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^
39+
>base : Base
40+
> : ^^^^
41+
>new : New & Base
42+
> : ^^^^^^^^^^
43+
>source : DerivedTable<{ base: Base; new: New; }>
44+
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^
45+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/62606
5+
6+
interface DerivedTable<S extends { base: any; new: any }> {
7+
// Error disappears when these property declarations are reversed
8+
schema: S["base"] & S["new"]
9+
readonlySchema: Readonly<S["base"] & S["new"]>
10+
}
11+
12+
interface Base { baseProp: number; }
13+
interface New { newProp: boolean; }
14+
15+
declare const source: DerivedTable<{ base: Base, new: New }>
16+
const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error

0 commit comments

Comments
 (0)