Skip to content

Commit fe8f239

Browse files
authored
Merge pull request microsoft#22869 from Microsoft/fixConditionalTypeVariance
Fix conditional type variance
2 parents d7df84d + 9acdb75 commit fe8f239

File tree

6 files changed

+709
-2
lines changed

6 files changed

+709
-2
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10200,8 +10200,11 @@ namespace ts {
1020010200
}
1020110201
else if (source.flags & TypeFlags.Conditional) {
1020210202
if (target.flags & TypeFlags.Conditional) {
10203-
if (isTypeIdenticalTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType) &&
10204-
isTypeIdenticalTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType)) {
10203+
// Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if
10204+
// one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2,
10205+
// and Y1 is related to Y2.
10206+
if (isTypeIdenticalTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType) &&
10207+
(isRelatedTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType) || isRelatedTo((<ConditionalType>target).checkType, (<ConditionalType>source).checkType))) {
1020510208
if (result = isRelatedTo(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target), reportErrors)) {
1020610209
result &= isRelatedTo(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target), reportErrors);
1020710210
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
tests/cases/conformance/types/conditional/conditionalTypes2.ts(15,5): error TS2322: Type 'Covariant<A>' is not assignable to type 'Covariant<B>'.
2+
Type 'A' is not assignable to type 'B'.
3+
tests/cases/conformance/types/conditional/conditionalTypes2.ts(19,5): error TS2322: Type 'Contravariant<B>' is not assignable to type 'Contravariant<A>'.
4+
Type 'A' is not assignable to type 'B'.
5+
tests/cases/conformance/types/conditional/conditionalTypes2.ts(24,5): error TS2322: Type 'Invariant<B>' is not assignable to type 'Invariant<A>'.
6+
Types of property 'foo' are incompatible.
7+
Type 'B extends string ? keyof B : B' is not assignable to type 'A extends string ? keyof A : A'.
8+
Type 'keyof B' is not assignable to type 'keyof A'.
9+
tests/cases/conformance/types/conditional/conditionalTypes2.ts(25,5): error TS2322: Type 'Invariant<A>' is not assignable to type 'Invariant<B>'.
10+
Types of property 'foo' are incompatible.
11+
Type 'A extends string ? keyof A : A' is not assignable to type 'B extends string ? keyof B : B'.
12+
Type 'A' is not assignable to type 'B'.
13+
14+
15+
==== tests/cases/conformance/types/conditional/conditionalTypes2.ts (4 errors) ====
16+
interface Covariant<T> {
17+
foo: T extends string ? T : number;
18+
}
19+
20+
interface Contravariant<T> {
21+
foo: T extends string ? keyof T : number;
22+
}
23+
24+
interface Invariant<T> {
25+
foo: T extends string ? keyof T : T;
26+
}
27+
28+
function f1<A, B extends A>(a: Covariant<A>, b: Covariant<B>) {
29+
a = b;
30+
b = a; // Error
31+
~
32+
!!! error TS2322: Type 'Covariant<A>' is not assignable to type 'Covariant<B>'.
33+
!!! error TS2322: Type 'A' is not assignable to type 'B'.
34+
}
35+
36+
function f2<A, B extends A>(a: Contravariant<A>, b: Contravariant<B>) {
37+
a = b; // Error
38+
~
39+
!!! error TS2322: Type 'Contravariant<B>' is not assignable to type 'Contravariant<A>'.
40+
!!! error TS2322: Type 'A' is not assignable to type 'B'.
41+
b = a;
42+
}
43+
44+
function f3<A, B extends A>(a: Invariant<A>, b: Invariant<B>) {
45+
a = b; // Error
46+
~
47+
!!! error TS2322: Type 'Invariant<B>' is not assignable to type 'Invariant<A>'.
48+
!!! error TS2322: Types of property 'foo' are incompatible.
49+
!!! error TS2322: Type 'B extends string ? keyof B : B' is not assignable to type 'A extends string ? keyof A : A'.
50+
!!! error TS2322: Type 'keyof B' is not assignable to type 'keyof A'.
51+
b = a; // Error
52+
~
53+
!!! error TS2322: Type 'Invariant<A>' is not assignable to type 'Invariant<B>'.
54+
!!! error TS2322: Types of property 'foo' are incompatible.
55+
!!! error TS2322: Type 'A extends string ? keyof A : A' is not assignable to type 'B extends string ? keyof B : B'.
56+
!!! error TS2322: Type 'A' is not assignable to type 'B'.
57+
}
58+
59+
// Repros from #22860
60+
61+
class Opt<T> {
62+
toVector(): Vector<T> {
63+
return <any>undefined;
64+
}
65+
}
66+
67+
interface Seq<T> {
68+
tail(): Opt<Seq<T>>;
69+
}
70+
71+
class Vector<T> implements Seq<T> {
72+
tail(): Opt<Vector<T>> {
73+
return <any>undefined;
74+
}
75+
partition2<U extends T>(predicate:(v:T)=>v is U): [Vector<U>,Vector<Exclude<T, U>>];
76+
partition2(predicate:(x:T)=>boolean): [Vector<T>,Vector<T>];
77+
partition2<U extends T>(predicate:(v:T)=>boolean): [Vector<U>,Vector<any>] {
78+
return <any>undefined;
79+
}
80+
}
81+
82+
interface A1<T> {
83+
bat: B1<A1<T>>;
84+
}
85+
86+
interface B1<T> extends A1<T> {
87+
bat: B1<B1<T>>;
88+
boom: T extends any ? true : true
89+
}
90+
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//// [conditionalTypes2.ts]
2+
interface Covariant<T> {
3+
foo: T extends string ? T : number;
4+
}
5+
6+
interface Contravariant<T> {
7+
foo: T extends string ? keyof T : number;
8+
}
9+
10+
interface Invariant<T> {
11+
foo: T extends string ? keyof T : T;
12+
}
13+
14+
function f1<A, B extends A>(a: Covariant<A>, b: Covariant<B>) {
15+
a = b;
16+
b = a; // Error
17+
}
18+
19+
function f2<A, B extends A>(a: Contravariant<A>, b: Contravariant<B>) {
20+
a = b; // Error
21+
b = a;
22+
}
23+
24+
function f3<A, B extends A>(a: Invariant<A>, b: Invariant<B>) {
25+
a = b; // Error
26+
b = a; // Error
27+
}
28+
29+
// Repros from #22860
30+
31+
class Opt<T> {
32+
toVector(): Vector<T> {
33+
return <any>undefined;
34+
}
35+
}
36+
37+
interface Seq<T> {
38+
tail(): Opt<Seq<T>>;
39+
}
40+
41+
class Vector<T> implements Seq<T> {
42+
tail(): Opt<Vector<T>> {
43+
return <any>undefined;
44+
}
45+
partition2<U extends T>(predicate:(v:T)=>v is U): [Vector<U>,Vector<Exclude<T, U>>];
46+
partition2(predicate:(x:T)=>boolean): [Vector<T>,Vector<T>];
47+
partition2<U extends T>(predicate:(v:T)=>boolean): [Vector<U>,Vector<any>] {
48+
return <any>undefined;
49+
}
50+
}
51+
52+
interface A1<T> {
53+
bat: B1<A1<T>>;
54+
}
55+
56+
interface B1<T> extends A1<T> {
57+
bat: B1<B1<T>>;
58+
boom: T extends any ? true : true
59+
}
60+
61+
62+
//// [conditionalTypes2.js]
63+
"use strict";
64+
function f1(a, b) {
65+
a = b;
66+
b = a; // Error
67+
}
68+
function f2(a, b) {
69+
a = b; // Error
70+
b = a;
71+
}
72+
function f3(a, b) {
73+
a = b; // Error
74+
b = a; // Error
75+
}
76+
// Repros from #22860
77+
var Opt = /** @class */ (function () {
78+
function Opt() {
79+
}
80+
Opt.prototype.toVector = function () {
81+
return undefined;
82+
};
83+
return Opt;
84+
}());
85+
var Vector = /** @class */ (function () {
86+
function Vector() {
87+
}
88+
Vector.prototype.tail = function () {
89+
return undefined;
90+
};
91+
Vector.prototype.partition2 = function (predicate) {
92+
return undefined;
93+
};
94+
return Vector;
95+
}());
96+
97+
98+
//// [conditionalTypes2.d.ts]
99+
interface Covariant<T> {
100+
foo: T extends string ? T : number;
101+
}
102+
interface Contravariant<T> {
103+
foo: T extends string ? keyof T : number;
104+
}
105+
interface Invariant<T> {
106+
foo: T extends string ? keyof T : T;
107+
}
108+
declare function f1<A, B extends A>(a: Covariant<A>, b: Covariant<B>): void;
109+
declare function f2<A, B extends A>(a: Contravariant<A>, b: Contravariant<B>): void;
110+
declare function f3<A, B extends A>(a: Invariant<A>, b: Invariant<B>): void;
111+
declare class Opt<T> {
112+
toVector(): Vector<T>;
113+
}
114+
interface Seq<T> {
115+
tail(): Opt<Seq<T>>;
116+
}
117+
declare class Vector<T> implements Seq<T> {
118+
tail(): Opt<Vector<T>>;
119+
partition2<U extends T>(predicate: (v: T) => v is U): [Vector<U>, Vector<Exclude<T, U>>];
120+
partition2(predicate: (x: T) => boolean): [Vector<T>, Vector<T>];
121+
}
122+
interface A1<T> {
123+
bat: B1<A1<T>>;
124+
}
125+
interface B1<T> extends A1<T> {
126+
bat: B1<B1<T>>;
127+
boom: T extends any ? true : true;
128+
}

0 commit comments

Comments
 (0)