Skip to content

Commit 8aa773b

Browse files
committed
Add rule that a type is assignable to A extends B ? C : D if it is
assignable to both C and D. (With some restrictions.) Fixes microsoft#26933.
1 parent 85a3475 commit 8aa773b

File tree

7 files changed

+324
-9
lines changed

7 files changed

+324
-9
lines changed

src/compiler/checker.ts

+29
Original file line numberDiff line numberDiff line change
@@ -11817,6 +11817,20 @@ namespace ts {
1181711817
return relation === definitelyAssignableRelation ? undefined : getConstraintOfType(type);
1181811818
}
1181911819

11820+
function mayBeNever(type: Type): boolean {
11821+
if (type.flags & TypeFlags.Intersection) {
11822+
return some((<IntersectionType>type).types, mayBeNever);
11823+
}
11824+
if (type.flags & TypeFlags.Union) {
11825+
return every((<UnionType>type).types, mayBeNever);
11826+
}
11827+
if (type.flags & TypeFlags.Instantiable) {
11828+
// Maybe we could be smarter in some cases.
11829+
return true;
11830+
}
11831+
return !!(type.flags & TypeFlags.Never);
11832+
}
11833+
1182011834
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
1182111835
const flags = source.flags & target.flags;
1182211836
if (relation === identityRelation && !(flags & TypeFlags.Object)) {
@@ -11918,6 +11932,21 @@ namespace ts {
1191811932
}
1191911933
}
1192011934
}
11935+
else if (target.flags & TypeFlags.Conditional) {
11936+
// A type T1 is related to a conditional type 'T2 extends U2 ? X2 : Y2' if the
11937+
// conditional type has no infer type parameters, T1 is related to both X2 and
11938+
// Y2, and (the conditional type is non-distributive or we know that T2 cannot
11939+
// instantiate to never).
11940+
if (!(<ConditionalType>target).root.inferTypeParameters &&
11941+
(!(<ConditionalType>target).root.isDistributive || !mayBeNever((<ConditionalType>target).checkType))) {
11942+
if (result = isRelatedTo(source, getTrueTypeFromConditionalType(<ConditionalType>target), reportErrors)) {
11943+
result &= isRelatedTo(source, getFalseTypeFromConditionalType(<ConditionalType>target), reportErrors);
11944+
}
11945+
if (result) {
11946+
return result;
11947+
}
11948+
}
11949+
}
1192111950

1192211951
if (source.flags & TypeFlags.TypeVariable) {
1192311952
if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {

tests/baselines/reference/conditionalTypes1.errors.txt

+32-8
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,35 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(104,5): error TS2
1818
tests/cases/conformance/types/conditional/conditionalTypes1.ts(106,5): error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>'.
1919
Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
2020
Type 'keyof T' is not assignable to type 'never'.
21-
Type 'string | number | symbol' is not assignable to type 'never'.
22-
Type 'string' is not assignable to type 'never'.
21+
Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'never'.
22+
Type 'keyof T' is not assignable to type 'never'.
23+
Type 'string | number | symbol' is not assignable to type 'never'.
24+
Type 'string' is not assignable to type 'never'.
2325
tests/cases/conformance/types/conditional/conditionalTypes1.ts(108,5): error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>'.
2426
Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
2527
Type 'keyof T' is not assignable to type 'never'.
28+
Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'never'.
29+
Type 'keyof T' is not assignable to type 'never'.
2630
tests/cases/conformance/types/conditional/conditionalTypes1.ts(114,5): error TS2322: Type 'keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
2731
Type 'string | number | symbol' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
2832
Type 'string' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
33+
Type 'string' is not assignable to type 'keyof T'.
34+
Type 'keyof T' is not assignable to type 'never'.
35+
Type 'string | number | symbol' is not assignable to type 'never'.
36+
Type 'string' is not assignable to type 'never'.
2937
tests/cases/conformance/types/conditional/conditionalTypes1.ts(115,5): error TS2322: Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
3038
Type 'keyof T' is not assignable to type 'never'.
31-
Type 'string | number | symbol' is not assignable to type 'never'.
32-
Type 'string' is not assignable to type 'never'.
39+
Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'never'.
40+
Type 'keyof T' is not assignable to type 'never'.
3341
tests/cases/conformance/types/conditional/conditionalTypes1.ts(116,5): error TS2322: Type 'keyof T' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
3442
Type 'string | number | symbol' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
3543
Type 'string' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
44+
Type 'string' is not assignable to type 'never'.
45+
Type 'keyof T' is not assignable to type 'never'.
3646
tests/cases/conformance/types/conditional/conditionalTypes1.ts(117,5): error TS2322: Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
3747
Type 'keyof T' is not assignable to type 'never'.
48+
Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'never'.
49+
Type 'keyof T' is not assignable to type 'never'.
3850
tests/cases/conformance/types/conditional/conditionalTypes1.ts(134,10): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
3951
tests/cases/conformance/types/conditional/conditionalTypes1.ts(135,5): error TS2542: Index signature in type 'DeepReadonlyArray<Part>' only permits reading.
4052
tests/cases/conformance/types/conditional/conditionalTypes1.ts(136,22): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
@@ -188,14 +200,18 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
188200
!!! error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>'.
189201
!!! error TS2322: Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
190202
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
191-
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'never'.
192-
!!! error TS2322: Type 'string' is not assignable to type 'never'.
203+
!!! error TS2322: Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'never'.
204+
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
205+
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'never'.
206+
!!! error TS2322: Type 'string' is not assignable to type 'never'.
193207
z = x;
194208
z = y; // Error
195209
~
196210
!!! error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>'.
197211
!!! error TS2322: Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
198212
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
213+
!!! error TS2322: Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'never'.
214+
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
199215
}
200216

201217
function f8<T>(x: keyof T, y: FunctionPropertyNames<T>, z: NonFunctionPropertyNames<T>) {
@@ -206,21 +222,29 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
206222
!!! error TS2322: Type 'keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
207223
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
208224
!!! error TS2322: Type 'string' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
225+
!!! error TS2322: Type 'string' is not assignable to type 'keyof T'.
226+
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
227+
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'never'.
228+
!!! error TS2322: Type 'string' is not assignable to type 'never'.
209229
y = z; // Error
210230
~
211231
!!! error TS2322: Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
212232
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
213-
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'never'.
214-
!!! error TS2322: Type 'string' is not assignable to type 'never'.
233+
!!! error TS2322: Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'never'.
234+
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
215235
z = x; // Error
216236
~
217237
!!! error TS2322: Type 'keyof T' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
218238
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
219239
!!! error TS2322: Type 'string' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
240+
!!! error TS2322: Type 'string' is not assignable to type 'never'.
241+
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
220242
z = y; // Error
221243
~
222244
!!! error TS2322: Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
223245
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
246+
!!! error TS2322: Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'never'.
247+
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
224248
}
225249

226250
type DeepReadonly<T> =

tests/baselines/reference/conditionalTypes2.errors.txt

+34-1
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(74,12): error TS2
2424
tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2345: Argument of type 'Extract2<T, Foo, Bar>' is not assignable to parameter of type '{ foo: string; bat: string; }'.
2525
Type 'T extends Bar ? T : never' is not assignable to type '{ foo: string; bat: string; }'.
2626
Type 'Bar & Foo & T' is not assignable to type '{ foo: string; bat: string; }'.
27+
tests/cases/conformance/types/conditional/conditionalTypes2.ts(163,11): error TS2322: Type '{ a: number; b: number; }' is not assignable to type '[T] extends [[infer U]] ? U : { b: number; }'.
28+
tests/cases/conformance/types/conditional/conditionalTypes2.ts(165,11): error TS2322: Type '{ a: number; b: number; }' is not assignable to type 'Distributive<T>'.
29+
tests/cases/conformance/types/conditional/conditionalTypes2.ts(167,11): error TS2322: Type '{ a: number; b: number; }' is not assignable to type 'Distributive<T & string>'.
2730

2831

29-
==== tests/cases/conformance/types/conditional/conditionalTypes2.ts (7 errors) ====
32+
==== tests/cases/conformance/types/conditional/conditionalTypes2.ts (10 errors) ====
3033
interface Covariant<T> {
3134
foo: T extends string ? T : number;
3235
}
@@ -206,4 +209,34 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2
206209

207210
type C2<T, V, E> =
208211
T extends object ? { [Q in keyof T]: C2<T[Q], V, E>; } : T;
212+
213+
// #26933
214+
215+
type Distributive<T> = T extends {a: number} ? { a: number } : { b: number };
216+
217+
function testAssignabilityToConditionalType<T>() {
218+
const o = { a: 1, b: 2 };
219+
const x: [T] extends [string] ? { y: number } : { a: number, b: number } = undefined!;
220+
// Simple case: OK
221+
const o1: [T] extends [number] ? { a: number } : { b: number } = o;
222+
// Simple case where source happens to be a conditional type: also OK
223+
const x1: [T] extends [number]
224+
? ([T] extends [string] ? { y: number } : { a: number })
225+
: ([T] extends [string] ? { y: number } : { b: number })
226+
= x;
227+
// Infer type parameters: no good
228+
const o2: [T] extends [[infer U]] ? U : { b: number } = o;
229+
~~
230+
!!! error TS2322: Type '{ a: number; b: number; }' is not assignable to type '[T] extends [[infer U]] ? U : { b: number; }'.
231+
// Distributive where T might instantiate to never: no good
232+
const o3: Distributive<T> = o;
233+
~~
234+
!!! error TS2322: Type '{ a: number; b: number; }' is not assignable to type 'Distributive<T>'.
235+
// Distributive where T & string might instantiate to never: also no good
236+
const o4: Distributive<T & string> = o;
237+
~~
238+
!!! error TS2322: Type '{ a: number; b: number; }' is not assignable to type 'Distributive<T & string>'.
239+
// Distributive where {a: T} cannot instantiate to never: OK
240+
const o5: Distributive<{a: T}> = o;
241+
}
209242

tests/baselines/reference/conditionalTypes2.js

+48
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,30 @@ type B2<T, V> =
145145

146146
type C2<T, V, E> =
147147
T extends object ? { [Q in keyof T]: C2<T[Q], V, E>; } : T;
148+
149+
// #26933
150+
151+
type Distributive<T> = T extends {a: number} ? { a: number } : { b: number };
152+
153+
function testAssignabilityToConditionalType<T>() {
154+
const o = { a: 1, b: 2 };
155+
const x: [T] extends [string] ? { y: number } : { a: number, b: number } = undefined!;
156+
// Simple case: OK
157+
const o1: [T] extends [number] ? { a: number } : { b: number } = o;
158+
// Simple case where source happens to be a conditional type: also OK
159+
const x1: [T] extends [number]
160+
? ([T] extends [string] ? { y: number } : { a: number })
161+
: ([T] extends [string] ? { y: number } : { b: number })
162+
= x;
163+
// Infer type parameters: no good
164+
const o2: [T] extends [[infer U]] ? U : { b: number } = o;
165+
// Distributive where T might instantiate to never: no good
166+
const o3: Distributive<T> = o;
167+
// Distributive where T & string might instantiate to never: also no good
168+
const o4: Distributive<T & string> = o;
169+
// Distributive where {a: T} cannot instantiate to never: OK
170+
const o5: Distributive<{a: T}> = o;
171+
}
148172

149173

150174
//// [conditionalTypes2.js]
@@ -222,6 +246,22 @@ function foo(value) {
222246
toString2(value);
223247
}
224248
}
249+
function testAssignabilityToConditionalType() {
250+
var o = { a: 1, b: 2 };
251+
var x = undefined;
252+
// Simple case: OK
253+
var o1 = o;
254+
// Simple case where source happens to be a conditional type: also OK
255+
var x1 = x;
256+
// Infer type parameters: no good
257+
var o2 = o;
258+
// Distributive where T might instantiate to never: no good
259+
var o3 = o;
260+
// Distributive where T & string might instantiate to never: also no good
261+
var o4 = o;
262+
// Distributive where {a: T} cannot instantiate to never: OK
263+
var o5 = o;
264+
}
225265

226266

227267
//// [conditionalTypes2.d.ts]
@@ -304,3 +344,11 @@ declare type B2<T, V> = T extends object ? T extends any[] ? T : {
304344
declare type C2<T, V, E> = T extends object ? {
305345
[Q in keyof T]: C2<T[Q], V, E>;
306346
} : T;
347+
declare type Distributive<T> = T extends {
348+
a: number;
349+
} ? {
350+
a: number;
351+
} : {
352+
b: number;
353+
};
354+
declare function testAssignabilityToConditionalType<T>(): void;

tests/baselines/reference/conditionalTypes2.symbols

+85
Original file line numberDiff line numberDiff line change
@@ -551,3 +551,88 @@ type C2<T, V, E> =
551551
>E : Symbol(E, Decl(conditionalTypes2.ts, 144, 13))
552552
>T : Symbol(T, Decl(conditionalTypes2.ts, 144, 8))
553553

554+
// #26933
555+
556+
type Distributive<T> = T extends {a: number} ? { a: number } : { b: number };
557+
>Distributive : Symbol(Distributive, Decl(conditionalTypes2.ts, 145, 63))
558+
>T : Symbol(T, Decl(conditionalTypes2.ts, 149, 18))
559+
>T : Symbol(T, Decl(conditionalTypes2.ts, 149, 18))
560+
>a : Symbol(a, Decl(conditionalTypes2.ts, 149, 34))
561+
>a : Symbol(a, Decl(conditionalTypes2.ts, 149, 48))
562+
>b : Symbol(b, Decl(conditionalTypes2.ts, 149, 64))
563+
564+
function testAssignabilityToConditionalType<T>() {
565+
>testAssignabilityToConditionalType : Symbol(testAssignabilityToConditionalType, Decl(conditionalTypes2.ts, 149, 77))
566+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
567+
568+
const o = { a: 1, b: 2 };
569+
>o : Symbol(o, Decl(conditionalTypes2.ts, 152, 9))
570+
>a : Symbol(a, Decl(conditionalTypes2.ts, 152, 15))
571+
>b : Symbol(b, Decl(conditionalTypes2.ts, 152, 21))
572+
573+
const x: [T] extends [string] ? { y: number } : { a: number, b: number } = undefined!;
574+
>x : Symbol(x, Decl(conditionalTypes2.ts, 153, 9))
575+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
576+
>y : Symbol(y, Decl(conditionalTypes2.ts, 153, 37))
577+
>a : Symbol(a, Decl(conditionalTypes2.ts, 153, 53))
578+
>b : Symbol(b, Decl(conditionalTypes2.ts, 153, 64))
579+
>undefined : Symbol(undefined)
580+
581+
// Simple case: OK
582+
const o1: [T] extends [number] ? { a: number } : { b: number } = o;
583+
>o1 : Symbol(o1, Decl(conditionalTypes2.ts, 155, 9))
584+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
585+
>a : Symbol(a, Decl(conditionalTypes2.ts, 155, 38))
586+
>b : Symbol(b, Decl(conditionalTypes2.ts, 155, 54))
587+
>o : Symbol(o, Decl(conditionalTypes2.ts, 152, 9))
588+
589+
// Simple case where source happens to be a conditional type: also OK
590+
const x1: [T] extends [number]
591+
>x1 : Symbol(x1, Decl(conditionalTypes2.ts, 157, 9))
592+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
593+
594+
? ([T] extends [string] ? { y: number } : { a: number })
595+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
596+
>y : Symbol(y, Decl(conditionalTypes2.ts, 158, 35))
597+
>a : Symbol(a, Decl(conditionalTypes2.ts, 158, 51))
598+
599+
: ([T] extends [string] ? { y: number } : { b: number })
600+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
601+
>y : Symbol(y, Decl(conditionalTypes2.ts, 159, 35))
602+
>b : Symbol(b, Decl(conditionalTypes2.ts, 159, 51))
603+
604+
= x;
605+
>x : Symbol(x, Decl(conditionalTypes2.ts, 153, 9))
606+
607+
// Infer type parameters: no good
608+
const o2: [T] extends [[infer U]] ? U : { b: number } = o;
609+
>o2 : Symbol(o2, Decl(conditionalTypes2.ts, 162, 9))
610+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
611+
>U : Symbol(U, Decl(conditionalTypes2.ts, 162, 33))
612+
>U : Symbol(U, Decl(conditionalTypes2.ts, 162, 33))
613+
>b : Symbol(b, Decl(conditionalTypes2.ts, 162, 45))
614+
>o : Symbol(o, Decl(conditionalTypes2.ts, 152, 9))
615+
616+
// Distributive where T might instantiate to never: no good
617+
const o3: Distributive<T> = o;
618+
>o3 : Symbol(o3, Decl(conditionalTypes2.ts, 164, 9))
619+
>Distributive : Symbol(Distributive, Decl(conditionalTypes2.ts, 145, 63))
620+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
621+
>o : Symbol(o, Decl(conditionalTypes2.ts, 152, 9))
622+
623+
// Distributive where T & string might instantiate to never: also no good
624+
const o4: Distributive<T & string> = o;
625+
>o4 : Symbol(o4, Decl(conditionalTypes2.ts, 166, 9))
626+
>Distributive : Symbol(Distributive, Decl(conditionalTypes2.ts, 145, 63))
627+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
628+
>o : Symbol(o, Decl(conditionalTypes2.ts, 152, 9))
629+
630+
// Distributive where {a: T} cannot instantiate to never: OK
631+
const o5: Distributive<{a: T}> = o;
632+
>o5 : Symbol(o5, Decl(conditionalTypes2.ts, 168, 9))
633+
>Distributive : Symbol(Distributive, Decl(conditionalTypes2.ts, 145, 63))
634+
>a : Symbol(a, Decl(conditionalTypes2.ts, 168, 28))
635+
>T : Symbol(T, Decl(conditionalTypes2.ts, 151, 44))
636+
>o : Symbol(o, Decl(conditionalTypes2.ts, 152, 9))
637+
}
638+

0 commit comments

Comments
 (0)