Skip to content

Commit 868a9ee

Browse files
authored
Merge pull request #21782 from Microsoft/fixIdenticalConditionalTypes
Compatibility of identical conditional types
2 parents e2178ec + 6dfcbff commit 868a9ee

File tree

6 files changed

+344
-6
lines changed

6 files changed

+344
-6
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8223,16 +8223,17 @@ namespace ts {
82238223
const erasedCheckType = getActualTypeParameter(checkType);
82248224
const trueType = instantiateType(baseTrueType, mapper);
82258225
const falseType = instantiateType(baseFalseType, mapper);
8226-
const id = target && (target.id + "," + erasedCheckType.id + "," + extendsType.id + "," + trueType.id + "," + falseType.id);
8227-
const cached = id && conditionalTypes.get(id);
8226+
// We compute the cache key from the ids of the four constituent types, plus an indicator of whether the
8227+
// type is distributive (i.e. whether the original declaration has a type parameter as the check type).
8228+
const isDistributive = (target ? target.checkType : erasedCheckType).flags & TypeFlags.TypeParameter ? 1 : 0;
8229+
const id = erasedCheckType.id + "," + extendsType.id + "," + trueType.id + "," + falseType.id + "," + isDistributive;
8230+
const cached = conditionalTypes.get(id);
82288231
if (cached) {
82298232
return cached;
82308233
}
82318234
const result = createConditionalType(erasedCheckType, extendsType, trueType, falseType,
82328235
inferTypeParameters, target, mapper, aliasSymbol, instantiateTypes(baseAliasTypeArguments, mapper));
8233-
if (id) {
8234-
conditionalTypes.set(id, result);
8235-
}
8236+
conditionalTypes.set(id, result);
82368237
return result;
82378238
}
82388239

tests/baselines/reference/conditionalTypes1.errors.txt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,10 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(156,5): error TS2
5858
tests/cases/conformance/types/conditional/conditionalTypes1.ts(157,5): error TS2322: Type 'T' is not assignable to type 'ZeroOf<T>'.
5959
Type 'string | number' is not assignable to type 'ZeroOf<T>'.
6060
Type 'string' is not assignable to type 'ZeroOf<T>'.
61+
tests/cases/conformance/types/conditional/conditionalTypes1.ts(247,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'z' must be of type 'T1', but here has type 'Foo<T & U>'.
6162

6263

63-
==== tests/cases/conformance/types/conditional/conditionalTypes1.ts (18 errors) ====
64+
==== tests/cases/conformance/types/conditional/conditionalTypes1.ts (19 errors) ====
6465
type Diff<T, U> = T extends U ? never : T;
6566
type Filter<T, U> = T extends U ? T : never;
6667
type NonNullable<T> = Diff<T, null | undefined>;
@@ -364,4 +365,36 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(157,5): error TS2
364365
type T81 = Eq2<true, false>; // false
365366
type T82 = Eq2<false, true>; // false
366367
type T83 = Eq2<false, false>; // true
368+
369+
// Repro from #21756
370+
371+
type Foo<T> = T extends string ? boolean : number;
372+
type Bar<T> = T extends string ? boolean : number;
373+
const convert = <U>(value: Foo<U>): Bar<U> => value;
374+
375+
type Baz<T> = Foo<T>;
376+
const convert2 = <T>(value: Foo<T>): Baz<T> => value;
377+
378+
function f31<T>() {
379+
type T1 = T extends string ? boolean : number;
380+
type T2 = T extends string ? boolean : number;
381+
var x: T1;
382+
var x: T2;
383+
}
384+
385+
function f32<T, U>() {
386+
type T1 = T & U extends string ? boolean : number;
387+
type T2 = Foo<T & U>;
388+
var z: T1;
389+
var z: T2; // Error, T2 is distributive, T1 isn't
390+
~
391+
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'z' must be of type 'T1', but here has type 'Foo<T & U>'.
392+
}
393+
394+
function f33<T, U>() {
395+
type T1 = Foo<T & U>;
396+
type T2 = Bar<T & U>;
397+
var z: T1;
398+
var z: T2;
399+
}
367400

tests/baselines/reference/conditionalTypes1.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,36 @@ type T80 = Eq2<true, true>; // true
224224
type T81 = Eq2<true, false>; // false
225225
type T82 = Eq2<false, true>; // false
226226
type T83 = Eq2<false, false>; // true
227+
228+
// Repro from #21756
229+
230+
type Foo<T> = T extends string ? boolean : number;
231+
type Bar<T> = T extends string ? boolean : number;
232+
const convert = <U>(value: Foo<U>): Bar<U> => value;
233+
234+
type Baz<T> = Foo<T>;
235+
const convert2 = <T>(value: Foo<T>): Baz<T> => value;
236+
237+
function f31<T>() {
238+
type T1 = T extends string ? boolean : number;
239+
type T2 = T extends string ? boolean : number;
240+
var x: T1;
241+
var x: T2;
242+
}
243+
244+
function f32<T, U>() {
245+
type T1 = T & U extends string ? boolean : number;
246+
type T2 = Foo<T & U>;
247+
var z: T1;
248+
var z: T2; // Error, T2 is distributive, T1 isn't
249+
}
250+
251+
function f33<T, U>() {
252+
type T1 = Foo<T & U>;
253+
type T2 = Bar<T & U>;
254+
var z: T1;
255+
var z: T2;
256+
}
227257

228258

229259
//// [conditionalTypes1.js]
@@ -285,6 +315,20 @@ function f21(x, y) {
285315
x = y; // Error
286316
y = x; // Error
287317
}
318+
var convert = function (value) { return value; };
319+
var convert2 = function (value) { return value; };
320+
function f31() {
321+
var x;
322+
var x;
323+
}
324+
function f32() {
325+
var z;
326+
var z; // Error, T2 is distributive, T1 isn't
327+
}
328+
function f33() {
329+
var z;
330+
var z;
331+
}
288332

289333

290334
//// [conditionalTypes1.d.ts]
@@ -450,3 +494,11 @@ declare type T80 = Eq2<true, true>;
450494
declare type T81 = Eq2<true, false>;
451495
declare type T82 = Eq2<false, true>;
452496
declare type T83 = Eq2<false, false>;
497+
declare type Foo<T> = T extends string ? boolean : number;
498+
declare type Bar<T> = T extends string ? boolean : number;
499+
declare const convert: <U>(value: Foo<U>) => Foo<U>;
500+
declare type Baz<T> = Foo<T>;
501+
declare const convert2: <T>(value: Foo<T>) => Foo<T>;
502+
declare function f31<T>(): void;
503+
declare function f32<T, U>(): void;
504+
declare function f33<T, U>(): void;

tests/baselines/reference/conditionalTypes1.symbols

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,3 +852,113 @@ type T83 = Eq2<false, false>; // true
852852
>T83 : Symbol(T83, Decl(conditionalTypes1.ts, 223, 28))
853853
>Eq2 : Symbol(Eq2, Decl(conditionalTypes1.ts, 218, 29))
854854

855+
// Repro from #21756
856+
857+
type Foo<T> = T extends string ? boolean : number;
858+
>Foo : Symbol(Foo, Decl(conditionalTypes1.ts, 224, 29))
859+
>T : Symbol(T, Decl(conditionalTypes1.ts, 228, 9))
860+
>T : Symbol(T, Decl(conditionalTypes1.ts, 228, 9))
861+
862+
type Bar<T> = T extends string ? boolean : number;
863+
>Bar : Symbol(Bar, Decl(conditionalTypes1.ts, 228, 50))
864+
>T : Symbol(T, Decl(conditionalTypes1.ts, 229, 9))
865+
>T : Symbol(T, Decl(conditionalTypes1.ts, 229, 9))
866+
867+
const convert = <U>(value: Foo<U>): Bar<U> => value;
868+
>convert : Symbol(convert, Decl(conditionalTypes1.ts, 230, 5))
869+
>U : Symbol(U, Decl(conditionalTypes1.ts, 230, 17))
870+
>value : Symbol(value, Decl(conditionalTypes1.ts, 230, 20))
871+
>Foo : Symbol(Foo, Decl(conditionalTypes1.ts, 224, 29))
872+
>U : Symbol(U, Decl(conditionalTypes1.ts, 230, 17))
873+
>Bar : Symbol(Bar, Decl(conditionalTypes1.ts, 228, 50))
874+
>U : Symbol(U, Decl(conditionalTypes1.ts, 230, 17))
875+
>value : Symbol(value, Decl(conditionalTypes1.ts, 230, 20))
876+
877+
type Baz<T> = Foo<T>;
878+
>Baz : Symbol(Baz, Decl(conditionalTypes1.ts, 230, 52))
879+
>T : Symbol(T, Decl(conditionalTypes1.ts, 232, 9))
880+
>Foo : Symbol(Foo, Decl(conditionalTypes1.ts, 224, 29))
881+
>T : Symbol(T, Decl(conditionalTypes1.ts, 232, 9))
882+
883+
const convert2 = <T>(value: Foo<T>): Baz<T> => value;
884+
>convert2 : Symbol(convert2, Decl(conditionalTypes1.ts, 233, 5))
885+
>T : Symbol(T, Decl(conditionalTypes1.ts, 233, 18))
886+
>value : Symbol(value, Decl(conditionalTypes1.ts, 233, 21))
887+
>Foo : Symbol(Foo, Decl(conditionalTypes1.ts, 224, 29))
888+
>T : Symbol(T, Decl(conditionalTypes1.ts, 233, 18))
889+
>Baz : Symbol(Baz, Decl(conditionalTypes1.ts, 230, 52))
890+
>T : Symbol(T, Decl(conditionalTypes1.ts, 233, 18))
891+
>value : Symbol(value, Decl(conditionalTypes1.ts, 233, 21))
892+
893+
function f31<T>() {
894+
>f31 : Symbol(f31, Decl(conditionalTypes1.ts, 233, 53))
895+
>T : Symbol(T, Decl(conditionalTypes1.ts, 235, 13))
896+
897+
type T1 = T extends string ? boolean : number;
898+
>T1 : Symbol(T1, Decl(conditionalTypes1.ts, 235, 19))
899+
>T : Symbol(T, Decl(conditionalTypes1.ts, 235, 13))
900+
901+
type T2 = T extends string ? boolean : number;
902+
>T2 : Symbol(T2, Decl(conditionalTypes1.ts, 236, 50))
903+
>T : Symbol(T, Decl(conditionalTypes1.ts, 235, 13))
904+
905+
var x: T1;
906+
>x : Symbol(x, Decl(conditionalTypes1.ts, 238, 7), Decl(conditionalTypes1.ts, 239, 7))
907+
>T1 : Symbol(T1, Decl(conditionalTypes1.ts, 235, 19))
908+
909+
var x: T2;
910+
>x : Symbol(x, Decl(conditionalTypes1.ts, 238, 7), Decl(conditionalTypes1.ts, 239, 7))
911+
>T2 : Symbol(T2, Decl(conditionalTypes1.ts, 236, 50))
912+
}
913+
914+
function f32<T, U>() {
915+
>f32 : Symbol(f32, Decl(conditionalTypes1.ts, 240, 1))
916+
>T : Symbol(T, Decl(conditionalTypes1.ts, 242, 13))
917+
>U : Symbol(U, Decl(conditionalTypes1.ts, 242, 15))
918+
919+
type T1 = T & U extends string ? boolean : number;
920+
>T1 : Symbol(T1, Decl(conditionalTypes1.ts, 242, 22))
921+
>T : Symbol(T, Decl(conditionalTypes1.ts, 242, 13))
922+
>U : Symbol(U, Decl(conditionalTypes1.ts, 242, 15))
923+
924+
type T2 = Foo<T & U>;
925+
>T2 : Symbol(T2, Decl(conditionalTypes1.ts, 243, 54))
926+
>Foo : Symbol(Foo, Decl(conditionalTypes1.ts, 224, 29))
927+
>T : Symbol(T, Decl(conditionalTypes1.ts, 242, 13))
928+
>U : Symbol(U, Decl(conditionalTypes1.ts, 242, 15))
929+
930+
var z: T1;
931+
>z : Symbol(z, Decl(conditionalTypes1.ts, 245, 7), Decl(conditionalTypes1.ts, 246, 7))
932+
>T1 : Symbol(T1, Decl(conditionalTypes1.ts, 242, 22))
933+
934+
var z: T2; // Error, T2 is distributive, T1 isn't
935+
>z : Symbol(z, Decl(conditionalTypes1.ts, 245, 7), Decl(conditionalTypes1.ts, 246, 7))
936+
>T2 : Symbol(T2, Decl(conditionalTypes1.ts, 243, 54))
937+
}
938+
939+
function f33<T, U>() {
940+
>f33 : Symbol(f33, Decl(conditionalTypes1.ts, 247, 1))
941+
>T : Symbol(T, Decl(conditionalTypes1.ts, 249, 13))
942+
>U : Symbol(U, Decl(conditionalTypes1.ts, 249, 15))
943+
944+
type T1 = Foo<T & U>;
945+
>T1 : Symbol(T1, Decl(conditionalTypes1.ts, 249, 22))
946+
>Foo : Symbol(Foo, Decl(conditionalTypes1.ts, 224, 29))
947+
>T : Symbol(T, Decl(conditionalTypes1.ts, 249, 13))
948+
>U : Symbol(U, Decl(conditionalTypes1.ts, 249, 15))
949+
950+
type T2 = Bar<T & U>;
951+
>T2 : Symbol(T2, Decl(conditionalTypes1.ts, 250, 25))
952+
>Bar : Symbol(Bar, Decl(conditionalTypes1.ts, 228, 50))
953+
>T : Symbol(T, Decl(conditionalTypes1.ts, 249, 13))
954+
>U : Symbol(U, Decl(conditionalTypes1.ts, 249, 15))
955+
956+
var z: T1;
957+
>z : Symbol(z, Decl(conditionalTypes1.ts, 252, 7), Decl(conditionalTypes1.ts, 253, 7))
958+
>T1 : Symbol(T1, Decl(conditionalTypes1.ts, 249, 22))
959+
960+
var z: T2;
961+
>z : Symbol(z, Decl(conditionalTypes1.ts, 252, 7), Decl(conditionalTypes1.ts, 253, 7))
962+
>T2 : Symbol(T2, Decl(conditionalTypes1.ts, 250, 25))
963+
}
964+

tests/baselines/reference/conditionalTypes1.types

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,3 +991,115 @@ type T83 = Eq2<false, false>; // true
991991
>false : false
992992
>false : false
993993

994+
// Repro from #21756
995+
996+
type Foo<T> = T extends string ? boolean : number;
997+
>Foo : Foo<T>
998+
>T : T
999+
>T : T
1000+
1001+
type Bar<T> = T extends string ? boolean : number;
1002+
>Bar : Bar<T>
1003+
>T : T
1004+
>T : T
1005+
1006+
const convert = <U>(value: Foo<U>): Bar<U> => value;
1007+
>convert : <U>(value: Foo<U>) => Foo<U>
1008+
><U>(value: Foo<U>): Bar<U> => value : <U>(value: Foo<U>) => Foo<U>
1009+
>U : U
1010+
>value : Foo<U>
1011+
>Foo : Foo<T>
1012+
>U : U
1013+
>Bar : Bar<T>
1014+
>U : U
1015+
>value : Foo<U>
1016+
1017+
type Baz<T> = Foo<T>;
1018+
>Baz : Foo<T>
1019+
>T : T
1020+
>Foo : Foo<T>
1021+
>T : T
1022+
1023+
const convert2 = <T>(value: Foo<T>): Baz<T> => value;
1024+
>convert2 : <T>(value: Foo<T>) => Foo<T>
1025+
><T>(value: Foo<T>): Baz<T> => value : <T>(value: Foo<T>) => Foo<T>
1026+
>T : T
1027+
>value : Foo<T>
1028+
>Foo : Foo<T>
1029+
>T : T
1030+
>Baz : Foo<T>
1031+
>T : T
1032+
>value : Foo<T>
1033+
1034+
function f31<T>() {
1035+
>f31 : <T>() => void
1036+
>T : T
1037+
1038+
type T1 = T extends string ? boolean : number;
1039+
>T1 : T extends string ? boolean : number
1040+
>T : T
1041+
1042+
type T2 = T extends string ? boolean : number;
1043+
>T2 : T extends string ? boolean : number
1044+
>T : T
1045+
1046+
var x: T1;
1047+
>x : T extends string ? boolean : number
1048+
>T1 : T extends string ? boolean : number
1049+
1050+
var x: T2;
1051+
>x : T extends string ? boolean : number
1052+
>T2 : T extends string ? boolean : number
1053+
}
1054+
1055+
function f32<T, U>() {
1056+
>f32 : <T, U>() => void
1057+
>T : T
1058+
>U : U
1059+
1060+
type T1 = T & U extends string ? boolean : number;
1061+
>T1 : T & U extends string ? boolean : number
1062+
>T : T
1063+
>U : U
1064+
1065+
type T2 = Foo<T & U>;
1066+
>T2 : Foo<T & U>
1067+
>Foo : Foo<T>
1068+
>T : T
1069+
>U : U
1070+
1071+
var z: T1;
1072+
>z : T & U extends string ? boolean : number
1073+
>T1 : T & U extends string ? boolean : number
1074+
1075+
var z: T2; // Error, T2 is distributive, T1 isn't
1076+
>z : T & U extends string ? boolean : number
1077+
>T2 : Foo<T & U>
1078+
}
1079+
1080+
function f33<T, U>() {
1081+
>f33 : <T, U>() => void
1082+
>T : T
1083+
>U : U
1084+
1085+
type T1 = Foo<T & U>;
1086+
>T1 : Foo<T & U>
1087+
>Foo : Foo<T>
1088+
>T : T
1089+
>U : U
1090+
1091+
type T2 = Bar<T & U>;
1092+
>T2 : Foo<T & U>
1093+
>Bar : Bar<T>
1094+
>T : T
1095+
>U : U
1096+
1097+
var z: T1;
1098+
>z : Foo<T & U>
1099+
>T1 : Foo<T & U>
1100+
1101+
var z: T2;
1102+
>z : Foo<T & U>
1103+
>T2 : Foo<T & U>
1104+
}
1105+

0 commit comments

Comments
 (0)