Skip to content

Commit 5d67f8e

Browse files
authored
Merge pull request #23645 from Microsoft/distributeKeyofUnion2
Distribute 'keyof' union types (take 2)
2 parents 59765e2 + f0c3291 commit 5d67f8e

10 files changed

+600
-197
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8268,7 +8268,8 @@ namespace ts {
82688268
}
82698269

82708270
function getIndexType(type: Type, stringsOnly = keyofStringsOnly): Type {
8271-
return type.flags & TypeFlags.Intersection ? getUnionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly))) :
8271+
return type.flags & TypeFlags.Union ? getIntersectionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly))) :
8272+
type.flags & TypeFlags.Intersection ? getUnionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly))) :
82728273
maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(<InstantiableType | UnionOrIntersectionType>type, stringsOnly) :
82738274
getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(<MappedType>type) :
82748275
type === wildcardType ? wildcardType :

tests/baselines/reference/keyofAndIndexedAccess.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,24 @@ function f3<T, K extends Extract<keyof T, string>>(t: T, k: K, tk: T[K]): void {
565565
type Predicates<TaggedRecord> = {
566566
[T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T]
567567
}
568+
569+
// Repro from #23618
570+
571+
type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }
572+
enum Flag {
573+
FLAG_1 = "flag_1",
574+
FLAG_2 = "flag_2"
575+
}
576+
577+
type SimpleDBRecord<Flag extends string> = { staticField: number } & DBBoolTable<Flag>
578+
function getFlagsFromSimpleRecord<Flag extends string>(record: SimpleDBRecord<Flag>, flags: Flag[]) {
579+
return record[flags[0]];
580+
}
581+
582+
type DynamicDBRecord<Flag extends string> = ({ dynamicField: number } | { dynamicField: string }) & DBBoolTable<Flag>
583+
function getFlagsFromDynamicRecord<Flag extends string>(record: DynamicDBRecord<Flag>, flags: Flag[]) {
584+
return record[flags[0]];
585+
}
568586

569587

570588
//// [keyofAndIndexedAccess.js]
@@ -948,6 +966,17 @@ function f3(t, k, tk) {
948966
t[key] = tk; // ok, T[K] ==> T[keyof T]
949967
}
950968
}
969+
var Flag;
970+
(function (Flag) {
971+
Flag["FLAG_1"] = "flag_1";
972+
Flag["FLAG_2"] = "flag_2";
973+
})(Flag || (Flag = {}));
974+
function getFlagsFromSimpleRecord(record, flags) {
975+
return record[flags[0]];
976+
}
977+
function getFlagsFromDynamicRecord(record, flags) {
978+
return record[flags[0]];
979+
}
951980

952981

953982
//// [keyofAndIndexedAccess.d.ts]
@@ -1212,3 +1241,20 @@ declare function f3<T, K extends Extract<keyof T, string>>(t: T, k: K, tk: T[K])
12121241
declare type Predicates<TaggedRecord> = {
12131242
[T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T];
12141243
};
1244+
declare type DBBoolTable<K extends string> = {
1245+
[k in K]: 0 | 1;
1246+
};
1247+
declare enum Flag {
1248+
FLAG_1 = "flag_1",
1249+
FLAG_2 = "flag_2"
1250+
}
1251+
declare type SimpleDBRecord<Flag extends string> = {
1252+
staticField: number;
1253+
} & DBBoolTable<Flag>;
1254+
declare function getFlagsFromSimpleRecord<Flag extends string>(record: SimpleDBRecord<Flag>, flags: Flag[]): SimpleDBRecord<Flag>[Flag];
1255+
declare type DynamicDBRecord<Flag extends string> = ({
1256+
dynamicField: number;
1257+
} | {
1258+
dynamicField: string;
1259+
}) & DBBoolTable<Flag>;
1260+
declare function getFlagsFromDynamicRecord<Flag extends string>(record: DynamicDBRecord<Flag>, flags: Flag[]): DynamicDBRecord<Flag>[Flag];

tests/baselines/reference/keyofAndIndexedAccess.symbols

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,3 +2010,64 @@ type Predicates<TaggedRecord> = {
20102010
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 564, 3))
20112011
}
20122012

2013+
// Repro from #23618
2014+
2015+
type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }
2016+
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 565, 1))
2017+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 569, 17))
2018+
>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 569, 40))
2019+
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 569, 17))
2020+
2021+
enum Flag {
2022+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 569, 56))
2023+
2024+
FLAG_1 = "flag_1",
2025+
>FLAG_1 : Symbol(Flag.FLAG_1, Decl(keyofAndIndexedAccess.ts, 570, 11))
2026+
2027+
FLAG_2 = "flag_2"
2028+
>FLAG_2 : Symbol(Flag.FLAG_2, Decl(keyofAndIndexedAccess.ts, 571, 22))
2029+
}
2030+
2031+
type SimpleDBRecord<Flag extends string> = { staticField: number } & DBBoolTable<Flag>
2032+
>SimpleDBRecord : Symbol(SimpleDBRecord, Decl(keyofAndIndexedAccess.ts, 573, 1))
2033+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 575, 20))
2034+
>staticField : Symbol(staticField, Decl(keyofAndIndexedAccess.ts, 575, 44))
2035+
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 565, 1))
2036+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 575, 20))
2037+
2038+
function getFlagsFromSimpleRecord<Flag extends string>(record: SimpleDBRecord<Flag>, flags: Flag[]) {
2039+
>getFlagsFromSimpleRecord : Symbol(getFlagsFromSimpleRecord, Decl(keyofAndIndexedAccess.ts, 575, 86))
2040+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 576, 34))
2041+
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 576, 55))
2042+
>SimpleDBRecord : Symbol(SimpleDBRecord, Decl(keyofAndIndexedAccess.ts, 573, 1))
2043+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 576, 34))
2044+
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 576, 84))
2045+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 576, 34))
2046+
2047+
return record[flags[0]];
2048+
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 576, 55))
2049+
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 576, 84))
2050+
}
2051+
2052+
type DynamicDBRecord<Flag extends string> = ({ dynamicField: number } | { dynamicField: string }) & DBBoolTable<Flag>
2053+
>DynamicDBRecord : Symbol(DynamicDBRecord, Decl(keyofAndIndexedAccess.ts, 578, 1))
2054+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 580, 21))
2055+
>dynamicField : Symbol(dynamicField, Decl(keyofAndIndexedAccess.ts, 580, 46))
2056+
>dynamicField : Symbol(dynamicField, Decl(keyofAndIndexedAccess.ts, 580, 73))
2057+
>DBBoolTable : Symbol(DBBoolTable, Decl(keyofAndIndexedAccess.ts, 565, 1))
2058+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 580, 21))
2059+
2060+
function getFlagsFromDynamicRecord<Flag extends string>(record: DynamicDBRecord<Flag>, flags: Flag[]) {
2061+
>getFlagsFromDynamicRecord : Symbol(getFlagsFromDynamicRecord, Decl(keyofAndIndexedAccess.ts, 580, 117))
2062+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 581, 35))
2063+
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 581, 56))
2064+
>DynamicDBRecord : Symbol(DynamicDBRecord, Decl(keyofAndIndexedAccess.ts, 578, 1))
2065+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 581, 35))
2066+
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 581, 86))
2067+
>Flag : Symbol(Flag, Decl(keyofAndIndexedAccess.ts, 581, 35))
2068+
2069+
return record[flags[0]];
2070+
>record : Symbol(record, Decl(keyofAndIndexedAccess.ts, 581, 56))
2071+
>flags : Symbol(flags, Decl(keyofAndIndexedAccess.ts, 581, 86))
2072+
}
2073+

tests/baselines/reference/keyofAndIndexedAccess.types

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -890,11 +890,11 @@ function f60<T>(source: T, target: T) {
890890
}
891891

892892
function f70(func: <T, U>(k1: keyof (T | U), k2: keyof (T & U)) => void) {
893-
>f70 : (func: <T, U>(k1: keyof (T | U), k2: keyof T | keyof U) => void) => void
894-
>func : <T, U>(k1: keyof (T | U), k2: keyof T | keyof U) => void
893+
>f70 : (func: <T, U>(k1: keyof T & keyof U, k2: keyof T | keyof U) => void) => void
894+
>func : <T, U>(k1: keyof T & keyof U, k2: keyof T | keyof U) => void
895895
>T : T
896896
>U : U
897-
>k1 : keyof (T | U)
897+
>k1 : keyof T & keyof U
898898
>T : T
899899
>U : U
900900
>k2 : keyof T | keyof U
@@ -903,7 +903,7 @@ function f70(func: <T, U>(k1: keyof (T | U), k2: keyof (T & U)) => void) {
903903

904904
func<{ a: any, b: any }, { a: any, c: any }>('a', 'a');
905905
>func<{ a: any, b: any }, { a: any, c: any }>('a', 'a') : void
906-
>func : <T, U>(k1: keyof (T | U), k2: keyof T | keyof U) => void
906+
>func : <T, U>(k1: keyof T & keyof U, k2: keyof T | keyof U) => void
907907
>a : any
908908
>b : any
909909
>a : any
@@ -913,7 +913,7 @@ function f70(func: <T, U>(k1: keyof (T | U), k2: keyof (T & U)) => void) {
913913

914914
func<{ a: any, b: any }, { a: any, c: any }>('a', 'b');
915915
>func<{ a: any, b: any }, { a: any, c: any }>('a', 'b') : void
916-
>func : <T, U>(k1: keyof (T | U), k2: keyof T | keyof U) => void
916+
>func : <T, U>(k1: keyof T & keyof U, k2: keyof T | keyof U) => void
917917
>a : any
918918
>b : any
919919
>a : any
@@ -923,7 +923,7 @@ function f70(func: <T, U>(k1: keyof (T | U), k2: keyof (T & U)) => void) {
923923

924924
func<{ a: any, b: any }, { a: any, c: any }>('a', 'c');
925925
>func<{ a: any, b: any }, { a: any, c: any }>('a', 'c') : void
926-
>func : <T, U>(k1: keyof (T | U), k2: keyof T | keyof U) => void
926+
>func : <T, U>(k1: keyof T & keyof U, k2: keyof T | keyof U) => void
927927
>a : any
928928
>b : any
929929
>a : any
@@ -1097,8 +1097,8 @@ function f73(func: <T, U, K extends keyof (T & U)>(x: T, y: U, k: K) => (T & U)[
10971097
}
10981098

10991099
function f74(func: <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[K]) {
1100-
>f74 : (func: <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[K]) => void
1101-
>func : <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[K]
1100+
>f74 : (func: <T, U, K extends keyof T & keyof U>(x: T, y: U, k: K) => (T | U)[K]) => void
1101+
>func : <T, U, K extends keyof T & keyof U>(x: T, y: U, k: K) => (T | U)[K]
11021102
>T : T
11031103
>U : U
11041104
>K : K
@@ -1117,7 +1117,7 @@ function f74(func: <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[
11171117
let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number
11181118
>a : number
11191119
>func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a') : number
1120-
>func : <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[K]
1120+
>func : <T, U, K extends keyof T & keyof U>(x: T, y: U, k: K) => (T | U)[K]
11211121
>{ a: 1, b: "hello" } : { a: number; b: string; }
11221122
>a : number
11231123
>1 : 1
@@ -1133,7 +1133,7 @@ function f74(func: <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[
11331133
let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean
11341134
>b : string | boolean
11351135
>func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b') : string | boolean
1136-
>func : <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[K]
1136+
>func : <T, U, K extends keyof T & keyof U>(x: T, y: U, k: K) => (T | U)[K]
11371137
>{ a: 1, b: "hello" } : { a: number; b: string; }
11381138
>a : number
11391139
>1 : 1
@@ -2345,3 +2345,72 @@ type Predicates<TaggedRecord> = {
23452345
>T : T
23462346
}
23472347

2348+
// Repro from #23618
2349+
2350+
type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }
2351+
>DBBoolTable : DBBoolTable<K>
2352+
>K : K
2353+
>k : k
2354+
>K : K
2355+
2356+
enum Flag {
2357+
>Flag : Flag
2358+
2359+
FLAG_1 = "flag_1",
2360+
>FLAG_1 : Flag.FLAG_1
2361+
>"flag_1" : "flag_1"
2362+
2363+
FLAG_2 = "flag_2"
2364+
>FLAG_2 : Flag.FLAG_2
2365+
>"flag_2" : "flag_2"
2366+
}
2367+
2368+
type SimpleDBRecord<Flag extends string> = { staticField: number } & DBBoolTable<Flag>
2369+
>SimpleDBRecord : SimpleDBRecord<Flag>
2370+
>Flag : Flag
2371+
>staticField : number
2372+
>DBBoolTable : DBBoolTable<K>
2373+
>Flag : Flag
2374+
2375+
function getFlagsFromSimpleRecord<Flag extends string>(record: SimpleDBRecord<Flag>, flags: Flag[]) {
2376+
>getFlagsFromSimpleRecord : <Flag extends string>(record: SimpleDBRecord<Flag>, flags: Flag[]) => SimpleDBRecord<Flag>[Flag]
2377+
>Flag : Flag
2378+
>record : SimpleDBRecord<Flag>
2379+
>SimpleDBRecord : SimpleDBRecord<Flag>
2380+
>Flag : Flag
2381+
>flags : Flag[]
2382+
>Flag : Flag
2383+
2384+
return record[flags[0]];
2385+
>record[flags[0]] : SimpleDBRecord<Flag>[Flag]
2386+
>record : SimpleDBRecord<Flag>
2387+
>flags[0] : Flag
2388+
>flags : Flag[]
2389+
>0 : 0
2390+
}
2391+
2392+
type DynamicDBRecord<Flag extends string> = ({ dynamicField: number } | { dynamicField: string }) & DBBoolTable<Flag>
2393+
>DynamicDBRecord : DynamicDBRecord<Flag>
2394+
>Flag : Flag
2395+
>dynamicField : number
2396+
>dynamicField : string
2397+
>DBBoolTable : DBBoolTable<K>
2398+
>Flag : Flag
2399+
2400+
function getFlagsFromDynamicRecord<Flag extends string>(record: DynamicDBRecord<Flag>, flags: Flag[]) {
2401+
>getFlagsFromDynamicRecord : <Flag extends string>(record: DynamicDBRecord<Flag>, flags: Flag[]) => DynamicDBRecord<Flag>[Flag]
2402+
>Flag : Flag
2403+
>record : DynamicDBRecord<Flag>
2404+
>DynamicDBRecord : DynamicDBRecord<Flag>
2405+
>Flag : Flag
2406+
>flags : Flag[]
2407+
>Flag : Flag
2408+
2409+
return record[flags[0]];
2410+
>record[flags[0]] : DynamicDBRecord<Flag>[Flag]
2411+
>record : DynamicDBRecord<Flag>
2412+
>flags[0] : Flag
2413+
>flags : Flag[]
2414+
>0 : 0
2415+
}
2416+

0 commit comments

Comments
 (0)