Skip to content

Commit 0f697c3

Browse files
authored
Merge pull request #21947 from Microsoft/fixKeyofWildcard
Fix issue with 'keyof T' and conditional types
2 parents a133cec + 9b227fc commit 0f697c3

File tree

6 files changed

+316
-0
lines changed

6 files changed

+316
-0
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7949,6 +7949,7 @@ namespace ts {
79497949
function getIndexType(type: Type): Type {
79507950
return maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(<InstantiableType | UnionOrIntersectionType>type) :
79517951
getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(<MappedType>type) :
7952+
type === wildcardType ? wildcardType :
79527953
type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType :
79537954
getLiteralTypeFromPropertyNames(type);
79547955
}

tests/baselines/reference/conditionalTypes1.errors.txt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,4 +447,34 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(275,43): error TS
447447
type A = Omit<{ a: void; b: never; }>; // 'a'
448448
type B = Omit2<{ a: void; b: never; }>; // 'a'
449449
}
450+
451+
// Repro from #21862
452+
453+
type OldDiff<T extends string, U extends string> = (
454+
& { [P in T]: P; }
455+
& { [P in U]: never; }
456+
& { [x: string]: never; }
457+
)[T];
458+
type NewDiff<T, U> = T extends U ? never : T;
459+
interface A {
460+
a: 'a';
461+
}
462+
interface B1 extends A {
463+
b: 'b';
464+
c: OldDiff<keyof this, keyof A>;
465+
}
466+
interface B2 extends A {
467+
b: 'b';
468+
c: NewDiff<keyof this, keyof A>;
469+
}
470+
type c1 = B1['c']; // 'c' | 'b'
471+
type c2 = B2['c']; // 'c' | 'b'
472+
473+
// Repro from #21929
474+
475+
type NonFooKeys1<T extends object> = OldDiff<keyof T, 'foo'>;
476+
type NonFooKeys2<T extends object> = Exclude<keyof T, 'foo'>;
477+
478+
type Test1 = NonFooKeys1<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
479+
type Test2 = NonFooKeys2<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
450480

tests/baselines/reference/conditionalTypes1.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,36 @@ function f50() {
285285
type A = Omit<{ a: void; b: never; }>; // 'a'
286286
type B = Omit2<{ a: void; b: never; }>; // 'a'
287287
}
288+
289+
// Repro from #21862
290+
291+
type OldDiff<T extends string, U extends string> = (
292+
& { [P in T]: P; }
293+
& { [P in U]: never; }
294+
& { [x: string]: never; }
295+
)[T];
296+
type NewDiff<T, U> = T extends U ? never : T;
297+
interface A {
298+
a: 'a';
299+
}
300+
interface B1 extends A {
301+
b: 'b';
302+
c: OldDiff<keyof this, keyof A>;
303+
}
304+
interface B2 extends A {
305+
b: 'b';
306+
c: NewDiff<keyof this, keyof A>;
307+
}
308+
type c1 = B1['c']; // 'c' | 'b'
309+
type c2 = B2['c']; // 'c' | 'b'
310+
311+
// Repro from #21929
312+
313+
type NonFooKeys1<T extends object> = OldDiff<keyof T, 'foo'>;
314+
type NonFooKeys2<T extends object> = Exclude<keyof T, 'foo'>;
315+
316+
type Test1 = NonFooKeys1<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
317+
type Test2 = NonFooKeys2<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
288318

289319

290320
//// [conditionalTypes1.js]
@@ -561,3 +591,36 @@ declare type T95<T> = T extends string ? boolean : number;
561591
declare const f44: <U>(value: T94<U>) => T95<U>;
562592
declare const f45: <U>(value: T95<U>) => T94<U>;
563593
declare function f50(): void;
594+
declare type OldDiff<T extends string, U extends string> = ({
595+
[P in T]: P;
596+
} & {
597+
[P in U]: never;
598+
} & {
599+
[x: string]: never;
600+
})[T];
601+
declare type NewDiff<T, U> = T extends U ? never : T;
602+
interface A {
603+
a: 'a';
604+
}
605+
interface B1 extends A {
606+
b: 'b';
607+
c: OldDiff<keyof this, keyof A>;
608+
}
609+
interface B2 extends A {
610+
b: 'b';
611+
c: NewDiff<keyof this, keyof A>;
612+
}
613+
declare type c1 = B1['c'];
614+
declare type c2 = B2['c'];
615+
declare type NonFooKeys1<T extends object> = OldDiff<keyof T, 'foo'>;
616+
declare type NonFooKeys2<T extends object> = Exclude<keyof T, 'foo'>;
617+
declare type Test1 = NonFooKeys1<{
618+
foo: 1;
619+
bar: 2;
620+
baz: 3;
621+
}>;
622+
declare type Test2 = NonFooKeys2<{
623+
foo: 1;
624+
bar: 2;
625+
baz: 3;
626+
}>;

tests/baselines/reference/conditionalTypes1.symbols

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,3 +1121,99 @@ function f50() {
11211121
>b : Symbol(b, Decl(conditionalTypes1.ts, 284, 29))
11221122
}
11231123

1124+
// Repro from #21862
1125+
1126+
type OldDiff<T extends string, U extends string> = (
1127+
>OldDiff : Symbol(OldDiff, Decl(conditionalTypes1.ts, 285, 1))
1128+
>T : Symbol(T, Decl(conditionalTypes1.ts, 289, 13))
1129+
>U : Symbol(U, Decl(conditionalTypes1.ts, 289, 30))
1130+
1131+
& { [P in T]: P; }
1132+
>P : Symbol(P, Decl(conditionalTypes1.ts, 290, 9))
1133+
>T : Symbol(T, Decl(conditionalTypes1.ts, 289, 13))
1134+
>P : Symbol(P, Decl(conditionalTypes1.ts, 290, 9))
1135+
1136+
& { [P in U]: never; }
1137+
>P : Symbol(P, Decl(conditionalTypes1.ts, 291, 9))
1138+
>U : Symbol(U, Decl(conditionalTypes1.ts, 289, 30))
1139+
1140+
& { [x: string]: never; }
1141+
>x : Symbol(x, Decl(conditionalTypes1.ts, 292, 9))
1142+
1143+
)[T];
1144+
>T : Symbol(T, Decl(conditionalTypes1.ts, 289, 13))
1145+
1146+
type NewDiff<T, U> = T extends U ? never : T;
1147+
>NewDiff : Symbol(NewDiff, Decl(conditionalTypes1.ts, 293, 5))
1148+
>T : Symbol(T, Decl(conditionalTypes1.ts, 294, 13))
1149+
>U : Symbol(U, Decl(conditionalTypes1.ts, 294, 15))
1150+
>T : Symbol(T, Decl(conditionalTypes1.ts, 294, 13))
1151+
>U : Symbol(U, Decl(conditionalTypes1.ts, 294, 15))
1152+
>T : Symbol(T, Decl(conditionalTypes1.ts, 294, 13))
1153+
1154+
interface A {
1155+
>A : Symbol(A, Decl(conditionalTypes1.ts, 294, 45))
1156+
1157+
a: 'a';
1158+
>a : Symbol(A.a, Decl(conditionalTypes1.ts, 295, 13))
1159+
}
1160+
interface B1 extends A {
1161+
>B1 : Symbol(B1, Decl(conditionalTypes1.ts, 297, 1))
1162+
>A : Symbol(A, Decl(conditionalTypes1.ts, 294, 45))
1163+
1164+
b: 'b';
1165+
>b : Symbol(B1.b, Decl(conditionalTypes1.ts, 298, 24))
1166+
1167+
c: OldDiff<keyof this, keyof A>;
1168+
>c : Symbol(B1.c, Decl(conditionalTypes1.ts, 299, 11))
1169+
>OldDiff : Symbol(OldDiff, Decl(conditionalTypes1.ts, 285, 1))
1170+
>A : Symbol(A, Decl(conditionalTypes1.ts, 294, 45))
1171+
}
1172+
interface B2 extends A {
1173+
>B2 : Symbol(B2, Decl(conditionalTypes1.ts, 301, 1))
1174+
>A : Symbol(A, Decl(conditionalTypes1.ts, 294, 45))
1175+
1176+
b: 'b';
1177+
>b : Symbol(B2.b, Decl(conditionalTypes1.ts, 302, 24))
1178+
1179+
c: NewDiff<keyof this, keyof A>;
1180+
>c : Symbol(B2.c, Decl(conditionalTypes1.ts, 303, 11))
1181+
>NewDiff : Symbol(NewDiff, Decl(conditionalTypes1.ts, 293, 5))
1182+
>A : Symbol(A, Decl(conditionalTypes1.ts, 294, 45))
1183+
}
1184+
type c1 = B1['c']; // 'c' | 'b'
1185+
>c1 : Symbol(c1, Decl(conditionalTypes1.ts, 305, 1))
1186+
>B1 : Symbol(B1, Decl(conditionalTypes1.ts, 297, 1))
1187+
1188+
type c2 = B2['c']; // 'c' | 'b'
1189+
>c2 : Symbol(c2, Decl(conditionalTypes1.ts, 306, 18))
1190+
>B2 : Symbol(B2, Decl(conditionalTypes1.ts, 301, 1))
1191+
1192+
// Repro from #21929
1193+
1194+
type NonFooKeys1<T extends object> = OldDiff<keyof T, 'foo'>;
1195+
>NonFooKeys1 : Symbol(NonFooKeys1, Decl(conditionalTypes1.ts, 307, 18))
1196+
>T : Symbol(T, Decl(conditionalTypes1.ts, 311, 17))
1197+
>OldDiff : Symbol(OldDiff, Decl(conditionalTypes1.ts, 285, 1))
1198+
>T : Symbol(T, Decl(conditionalTypes1.ts, 311, 17))
1199+
1200+
type NonFooKeys2<T extends object> = Exclude<keyof T, 'foo'>;
1201+
>NonFooKeys2 : Symbol(NonFooKeys2, Decl(conditionalTypes1.ts, 311, 61))
1202+
>T : Symbol(T, Decl(conditionalTypes1.ts, 312, 17))
1203+
>Exclude : Symbol(Exclude, Decl(lib.d.ts, --, --))
1204+
>T : Symbol(T, Decl(conditionalTypes1.ts, 312, 17))
1205+
1206+
type Test1 = NonFooKeys1<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
1207+
>Test1 : Symbol(Test1, Decl(conditionalTypes1.ts, 312, 61))
1208+
>NonFooKeys1 : Symbol(NonFooKeys1, Decl(conditionalTypes1.ts, 307, 18))
1209+
>foo : Symbol(foo, Decl(conditionalTypes1.ts, 314, 26))
1210+
>bar : Symbol(bar, Decl(conditionalTypes1.ts, 314, 33))
1211+
>baz : Symbol(baz, Decl(conditionalTypes1.ts, 314, 41))
1212+
1213+
type Test2 = NonFooKeys2<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
1214+
>Test2 : Symbol(Test2, Decl(conditionalTypes1.ts, 314, 51))
1215+
>NonFooKeys2 : Symbol(NonFooKeys2, Decl(conditionalTypes1.ts, 311, 61))
1216+
>foo : Symbol(foo, Decl(conditionalTypes1.ts, 315, 26))
1217+
>bar : Symbol(bar, Decl(conditionalTypes1.ts, 315, 33))
1218+
>baz : Symbol(baz, Decl(conditionalTypes1.ts, 315, 41))
1219+

tests/baselines/reference/conditionalTypes1.types

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,3 +1274,99 @@ function f50() {
12741274
>b : never
12751275
}
12761276

1277+
// Repro from #21862
1278+
1279+
type OldDiff<T extends string, U extends string> = (
1280+
>OldDiff : ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[T]
1281+
>T : T
1282+
>U : U
1283+
1284+
& { [P in T]: P; }
1285+
>P : P
1286+
>T : T
1287+
>P : P
1288+
1289+
& { [P in U]: never; }
1290+
>P : P
1291+
>U : U
1292+
1293+
& { [x: string]: never; }
1294+
>x : string
1295+
1296+
)[T];
1297+
>T : T
1298+
1299+
type NewDiff<T, U> = T extends U ? never : T;
1300+
>NewDiff : NewDiff<T, U>
1301+
>T : T
1302+
>U : U
1303+
>T : T
1304+
>U : U
1305+
>T : T
1306+
1307+
interface A {
1308+
>A : A
1309+
1310+
a: 'a';
1311+
>a : "a"
1312+
}
1313+
interface B1 extends A {
1314+
>B1 : B1
1315+
>A : A
1316+
1317+
b: 'b';
1318+
>b : "b"
1319+
1320+
c: OldDiff<keyof this, keyof A>;
1321+
>c : ({ [P in keyof this]: P; } & { a: never; } & { [x: string]: never; })[keyof this]
1322+
>OldDiff : ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[T]
1323+
>A : A
1324+
}
1325+
interface B2 extends A {
1326+
>B2 : B2
1327+
>A : A
1328+
1329+
b: 'b';
1330+
>b : "b"
1331+
1332+
c: NewDiff<keyof this, keyof A>;
1333+
>c : NewDiff<keyof this, "a">
1334+
>NewDiff : NewDiff<T, U>
1335+
>A : A
1336+
}
1337+
type c1 = B1['c']; // 'c' | 'b'
1338+
>c1 : "b" | "c"
1339+
>B1 : B1
1340+
1341+
type c2 = B2['c']; // 'c' | 'b'
1342+
>c2 : "b" | "c"
1343+
>B2 : B2
1344+
1345+
// Repro from #21929
1346+
1347+
type NonFooKeys1<T extends object> = OldDiff<keyof T, 'foo'>;
1348+
>NonFooKeys1 : ({ [P in keyof T]: P; } & { foo: never; } & { [x: string]: never; })[keyof T]
1349+
>T : T
1350+
>OldDiff : ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[T]
1351+
>T : T
1352+
1353+
type NonFooKeys2<T extends object> = Exclude<keyof T, 'foo'>;
1354+
>NonFooKeys2 : Exclude<keyof T, "foo">
1355+
>T : T
1356+
>Exclude : Exclude<T, U>
1357+
>T : T
1358+
1359+
type Test1 = NonFooKeys1<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
1360+
>Test1 : "bar" | "baz"
1361+
>NonFooKeys1 : ({ [P in keyof T]: P; } & { foo: never; } & { [x: string]: never; })[keyof T]
1362+
>foo : 1
1363+
>bar : 2
1364+
>baz : 3
1365+
1366+
type Test2 = NonFooKeys2<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
1367+
>Test2 : "bar" | "baz"
1368+
>NonFooKeys2 : Exclude<keyof T, "foo">
1369+
>foo : 1
1370+
>bar : 2
1371+
>baz : 3
1372+

tests/cases/conformance/types/conditional/conditionalTypes1.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,33 @@ function f50() {
287287
type A = Omit<{ a: void; b: never; }>; // 'a'
288288
type B = Omit2<{ a: void; b: never; }>; // 'a'
289289
}
290+
291+
// Repro from #21862
292+
293+
type OldDiff<T extends string, U extends string> = (
294+
& { [P in T]: P; }
295+
& { [P in U]: never; }
296+
& { [x: string]: never; }
297+
)[T];
298+
type NewDiff<T, U> = T extends U ? never : T;
299+
interface A {
300+
a: 'a';
301+
}
302+
interface B1 extends A {
303+
b: 'b';
304+
c: OldDiff<keyof this, keyof A>;
305+
}
306+
interface B2 extends A {
307+
b: 'b';
308+
c: NewDiff<keyof this, keyof A>;
309+
}
310+
type c1 = B1['c']; // 'c' | 'b'
311+
type c2 = B2['c']; // 'c' | 'b'
312+
313+
// Repro from #21929
314+
315+
type NonFooKeys1<T extends object> = OldDiff<keyof T, 'foo'>;
316+
type NonFooKeys2<T extends object> = Exclude<keyof T, 'foo'>;
317+
318+
type Test1 = NonFooKeys1<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"
319+
type Test2 = NonFooKeys2<{foo: 1, bar: 2, baz: 3}>; // "bar" | "baz"

0 commit comments

Comments
 (0)