• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// @strictNullChecks: true
2// @declaration: true
3
4class Shape {
5    name: string;
6    width: number;
7    height: number;
8    visible: boolean;
9}
10
11class TaggedShape extends Shape {
12    tag: string;
13}
14
15class Item {
16    name: string;
17    price: number;
18}
19
20class Options {
21    visible: "yes" | "no";
22}
23
24type Dictionary<T> = { [x: string]: T };
25type NumericallyIndexed<T> = { [x: number]: T };
26
27const enum E { A, B, C }
28
29type K00 = keyof any;  // string
30type K01 = keyof string;  // "toString" | "charAt" | ...
31type K02 = keyof number;  // "toString" | "toFixed" | "toExponential" | ...
32type K03 = keyof boolean;  // "valueOf"
33type K04 = keyof void;  // never
34type K05 = keyof undefined;  // never
35type K06 = keyof null;  // never
36type K07 = keyof never;  // string | number | symbol
37type K08 = keyof unknown; // never
38
39type K10 = keyof Shape;  // "name" | "width" | "height" | "visible"
40type K11 = keyof Shape[];  // "length" | "toString" | ...
41type K12 = keyof Dictionary<Shape>;  // string
42type K13 = keyof {};  // never
43type K14 = keyof Object;  // "constructor" | "toString" | ...
44type K15 = keyof E;  // "toString" | "toFixed" | "toExponential" | ...
45type K16 = keyof [string, number];  // "0" | "1" | "length" | "toString" | ...
46type K17 = keyof (Shape | Item);  // "name"
47type K18 = keyof (Shape & Item);  // "name" | "width" | "height" | "visible" | "price"
48type K19 = keyof NumericallyIndexed<Shape> // never
49
50type KeyOf<T> = keyof T;
51
52type K20 = KeyOf<Shape>;  // "name" | "width" | "height" | "visible"
53type K21 = KeyOf<Dictionary<Shape>>;  // string
54
55type NAME = "name";
56type WIDTH_OR_HEIGHT = "width" | "height";
57
58type Q10 = Shape["name"];  // string
59type Q11 = Shape["width" | "height"];  // number
60type Q12 = Shape["name" | "visible"];  // string | boolean
61
62type Q20 = Shape[NAME];  // string
63type Q21 = Shape[WIDTH_OR_HEIGHT];  // number
64
65type Q30 = [string, number][0];  // string
66type Q31 = [string, number][1];  // number
67type Q32 = [string, number][number];  // string | number
68type Q33 = [string, number][E.A];  // string
69type Q34 = [string, number][E.B];  // number
70type Q35 = [string, number]["0"];  // string
71type Q36 = [string, number]["1"];  // string
72
73type Q40 = (Shape | Options)["visible"];  // boolean | "yes" | "no"
74type Q41 = (Shape & Options)["visible"];  // true & "yes" | true & "no" | false & "yes" | false & "no"
75
76type Q50 = Dictionary<Shape>["howdy"];  // Shape
77type Q51 = Dictionary<Shape>[123];  // Shape
78type Q52 = Dictionary<Shape>[E.B];  // Shape
79
80declare let cond: boolean;
81
82function getProperty<T, K extends keyof T>(obj: T, key: K) {
83    return obj[key];
84}
85
86function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
87    obj[key] = value;
88}
89
90function f10(shape: Shape) {
91    let name = getProperty(shape, "name");  // string
92    let widthOrHeight = getProperty(shape, cond ? "width" : "height");  // number
93    let nameOrVisible = getProperty(shape, cond ? "name" : "visible");  // string | boolean
94    setProperty(shape, "name", "rectangle");
95    setProperty(shape, cond ? "width" : "height", 10);
96    setProperty(shape, cond ? "name" : "visible", true);  // Technically not safe
97}
98
99function f11(a: Shape[]) {
100    let len = getProperty(a, "length");  // number
101    setProperty(a, "length", len);
102}
103
104function f12(t: [Shape, boolean]) {
105    let len = getProperty(t, "length");
106    let s2 = getProperty(t, "0");  // Shape
107    let b2 = getProperty(t, "1");  // boolean
108}
109
110function f13(foo: any, bar: any) {
111    let x = getProperty(foo, "x");  // any
112    let y = getProperty(foo, "100");  // any
113    let z = getProperty(foo, bar);  // any
114}
115
116class Component<PropType> {
117    props: PropType;
118    getProperty<K extends keyof PropType>(key: K) {
119        return this.props[key];
120    }
121    setProperty<K extends keyof PropType>(key: K, value: PropType[K]) {
122        this.props[key] = value;
123    }
124}
125
126function f20(component: Component<Shape>) {
127    let name = component.getProperty("name");  // string
128    let widthOrHeight = component.getProperty(cond ? "width" : "height");  // number
129    let nameOrVisible = component.getProperty(cond ? "name" : "visible");  // string | boolean
130    component.setProperty("name", "rectangle");
131    component.setProperty(cond ? "width" : "height", 10)
132    component.setProperty(cond ? "name" : "visible", true);  // Technically not safe
133}
134
135function pluck<T, K extends keyof T>(array: T[], key: K) {
136    return array.map(x => x[key]);
137}
138
139function f30(shapes: Shape[]) {
140    let names = pluck(shapes, "name");    // string[]
141    let widths = pluck(shapes, "width");  // number[]
142    let nameOrVisibles = pluck(shapes, cond ? "name" : "visible");  // (string | boolean)[]
143}
144
145function f31<K extends keyof Shape>(key: K) {
146    const shape: Shape = { name: "foo", width: 5, height: 10, visible: true };
147    return shape[key];  // Shape[K]
148}
149
150function f32<K extends "width" | "height">(key: K) {
151    const shape: Shape = { name: "foo", width: 5, height: 10, visible: true };
152    return shape[key];  // Shape[K]
153}
154
155function f33<S extends Shape, K extends keyof S>(shape: S, key: K) {
156    let name = getProperty(shape, "name");
157    let prop = getProperty(shape, key);
158    return prop;
159}
160
161function f34(ts: TaggedShape) {
162    let tag1 = f33(ts, "tag");
163    let tag2 = getProperty(ts, "tag");
164}
165
166class C {
167    public x: string;
168    protected y: string;
169    private z: string;
170}
171
172// Indexed access expressions have always permitted access to private and protected members.
173// For consistency we also permit such access in indexed access types.
174function f40(c: C) {
175    type X = C["x"];
176    type Y = C["y"];
177    type Z = C["z"];
178    let x: X = c["x"];
179    let y: Y = c["y"];
180    let z: Z = c["z"];
181}
182
183function f50<T>(k: keyof T, s: string) {
184    const x1 = s as keyof T;
185    const x2 = k as string;
186}
187
188function f51<T, K extends keyof T>(k: K, s: string) {
189    const x1 = s as keyof T;
190    const x2 = k as string;
191}
192
193function f52<T>(obj: { [x: string]: boolean }, k: Exclude<keyof T, symbol>, s: string, n: number) {
194    const x1 = obj[s];
195    const x2 = obj[n];
196    const x3 = obj[k];
197}
198
199function f53<T, K extends Exclude<keyof T, symbol>>(obj: { [x: string]: boolean }, k: K, s: string, n: number) {
200    const x1 = obj[s];
201    const x2 = obj[n];
202    const x3 = obj[k];
203}
204
205function f54<T>(obj: T, key: keyof T) {
206    for (let s in obj[key]) {
207    }
208    const b = "foo" in obj[key];
209}
210
211function f55<T, K extends keyof T>(obj: T, key: K) {
212    for (let s in obj[key]) {
213    }
214    const b = "foo" in obj[key];
215}
216
217function f60<T>(source: T, target: T) {
218    for (let k in source) {
219        target[k] = source[k];
220    }
221}
222
223function f70(func: <T, U>(k1: keyof (T | U), k2: keyof (T & U)) => void) {
224    func<{ a: any, b: any }, { a: any, c: any }>('a', 'a');
225    func<{ a: any, b: any }, { a: any, c: any }>('a', 'b');
226    func<{ a: any, b: any }, { a: any, c: any }>('a', 'c');
227}
228
229function f71(func: <T, U>(x: T, y: U) => Partial<T & U>) {
230    let x = func({ a: 1, b: "hello" }, { c: true });
231    x.a;  // number | undefined
232    x.b;  // string | undefined
233    x.c;  // boolean | undefined
234}
235
236function f72(func: <T, U, K extends keyof T | keyof U>(x: T, y: U, k: K) => (T & U)[K]) {
237    let a = func({ a: 1, b: "hello" }, { c: true }, 'a');  // number
238    let b = func({ a: 1, b: "hello" }, { c: true }, 'b');  // string
239    let c = func({ a: 1, b: "hello" }, { c: true }, 'c');  // boolean
240}
241
242function f73(func: <T, U, K extends keyof (T & U)>(x: T, y: U, k: K) => (T & U)[K]) {
243    let a = func({ a: 1, b: "hello" }, { c: true }, 'a');  // number
244    let b = func({ a: 1, b: "hello" }, { c: true }, 'b');  // string
245    let c = func({ a: 1, b: "hello" }, { c: true }, 'c');  // boolean
246}
247
248function f74(func: <T, U, K extends keyof (T | U)>(x: T, y: U, k: K) => (T | U)[K]) {
249    let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a');  // number
250    let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b');  // string | boolean
251}
252
253function f80<T extends { a: { x: any } }>(obj: T) {
254    let a1 = obj.a;  // { x: any }
255    let a2 = obj['a'];  // { x: any }
256    let a3 = obj['a'] as T['a'];  // T["a"]
257    let x1 = obj.a.x;  // any
258    let x2 = obj['a']['x'];  // any
259    let x3 = obj['a']['x'] as T['a']['x'];  // T["a"]["x"]
260}
261
262function f81<T extends { a: { x: any } }>(obj: T) {
263    return obj['a']['x'] as T['a']['x'];
264}
265
266function f82() {
267    let x1 = f81({ a: { x: "hello" } });  // string
268    let x2 = f81({ a: { x: 42 } });  // number
269}
270
271function f83<T extends { [x: string]: { x: any } }, K extends keyof T>(obj: T, key: K) {
272    return obj[key]['x'] as T[K]['x'];
273}
274
275function f84() {
276    let x1 = f83({ foo: { x: "hello" } }, "foo");  // string
277    let x2 = f83({ bar: { x: 42 } }, "bar");  // number
278}
279
280class C1 {
281    x: number;
282    get<K extends keyof this>(key: K) {
283        return this[key];
284    }
285    set<K extends keyof this>(key: K, value: this[K]) {
286        this[key] = value;
287    }
288    foo() {
289        let x1 = this.x;  // number
290        let x2 = this["x"];  // number
291        let x3 = this.get("x");  // this["x"]
292        let x4 = getProperty(this, "x"); // this["x"]
293        this.x = 42;
294        this["x"] = 42;
295        this.set("x", 42);
296        setProperty(this, "x", 42);
297    }
298}
299
300type S2 = {
301    a: string;
302    b: string;
303};
304
305function f90<T extends S2, K extends keyof S2>(x1: S2[keyof S2], x2: T[keyof S2], x3: S2[K]) {
306    x1 = x2;
307    x1 = x3;
308    x2 = x1;
309    x2 = x3;
310    x3 = x1;
311    x3 = x2;
312    x1.length;
313    x2.length;
314    x3.length;
315}
316
317function f91<T, K extends keyof T>(x: T, y: T[keyof T], z: T[K]) {
318    let a: {};
319    a = x;
320    a = y;
321    a = z;
322}
323
324function f92<T, K extends keyof T>(x: T, y: T[keyof T], z: T[K]) {
325    let a: {} | null | undefined;
326    a = x;
327    a = y;
328    a = z;
329}
330
331// Repros from #12011
332
333class Base {
334    get<K extends keyof this>(prop: K) {
335        return this[prop];
336    }
337    set<K extends keyof this>(prop: K, value: this[K]) {
338        this[prop] = value;
339    }
340}
341
342class Person extends Base {
343    parts: number;
344    constructor(parts: number) {
345        super();
346        this.set("parts", parts);
347    }
348    getParts() {
349        return this.get("parts")
350    }
351}
352
353class OtherPerson {
354    parts: number;
355    constructor(parts: number) {
356        setProperty(this, "parts", parts);
357    }
358    getParts() {
359        return getProperty(this, "parts")
360    }
361}
362
363// Modified repro from #12544
364
365function path<T, K1 extends keyof T>(obj: T, key1: K1): T[K1];
366function path<T, K1 extends keyof T, K2 extends keyof T[K1]>(obj: T, key1: K1, key2: K2): T[K1][K2];
367function path<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]>(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3];
368function path(obj: any, ...keys: (string | number)[]): any;
369function path(obj: any, ...keys: (string | number)[]): any {
370    let result = obj;
371    for (let k of keys) {
372        result = result[k];
373    }
374    return result;
375}
376
377type Thing = {
378    a: { x: number, y: string },
379    b: boolean
380};
381
382
383function f1(thing: Thing) {
384    let x1 = path(thing, 'a');  // { x: number, y: string }
385    let x2 = path(thing, 'a', 'y');  // string
386    let x3 = path(thing, 'b');  // boolean
387    let x4 = path(thing, ...['a', 'x']);  // any
388}
389
390// Repro from comment in #12114
391
392const assignTo2 = <T, K1 extends keyof T, K2 extends keyof T[K1]>(object: T, key1: K1, key2: K2) =>
393    (value: T[K1][K2]) => object[key1][key2] = value;
394
395// Modified repro from #12573
396
397declare function one<T>(handler: (t: T) => void): T
398var empty = one(() => {}) // inferred as {}, expected
399
400type Handlers<T> = { [K in keyof T]: (t: T[K]) => void }
401declare function on<T>(handlerHash: Handlers<T>): T
402var hashOfEmpty1 = on({ test: () => {} });  // {}
403var hashOfEmpty2 = on({ test: (x: boolean) => {} });  // { test: boolean }
404
405// Repro from #12624
406
407interface Options1<Data, Computed> {
408    data?: Data
409    computed?: Computed;
410}
411
412declare class Component1<Data, Computed> {
413    constructor(options: Options1<Data, Computed>);
414    get<K extends keyof (Data & Computed)>(key: K): (Data & Computed)[K];
415}
416
417let c1 = new Component1({
418    data: {
419        hello: ""
420    }
421});
422
423c1.get("hello");
424
425// Repro from #12625
426
427interface Options2<Data, Computed> {
428    data?: Data
429    computed?: Computed;
430}
431
432declare class Component2<Data, Computed> {
433    constructor(options: Options2<Data, Computed>);
434    get<K extends keyof Data | keyof Computed>(key: K): (Data & Computed)[K];
435}
436
437// Repro from #12641
438
439interface R {
440    p: number;
441}
442
443function f<K extends keyof R>(p: K) {
444    let a: any;
445    a[p].add;  // any
446}
447
448// Repro from #12651
449
450type MethodDescriptor = {
451	name: string;
452	args: any[];
453	returnValue: any;
454}
455
456declare function dispatchMethod<M extends MethodDescriptor>(name: M['name'], args: M['args']): M['returnValue'];
457
458type SomeMethodDescriptor = {
459	name: "someMethod";
460	args: [string, number];
461	returnValue: string[];
462}
463
464let result = dispatchMethod<SomeMethodDescriptor>("someMethod", ["hello", 35]);
465
466// Repro from #13073
467
468type KeyTypes = "a" | "b"
469let MyThingy: { [key in KeyTypes]: string[] };
470
471function addToMyThingy<S extends KeyTypes>(key: S) {
472    MyThingy[key].push("a");
473}
474
475// Repro from #13102
476
477type Handler<T> = {
478    onChange: (name: keyof T) => void;
479};
480
481function onChangeGenericFunction<T>(handler: Handler<T & {preset: number}>) {
482    handler.onChange('preset')
483}
484
485// Repro from #13285
486
487function updateIds<T extends Record<K, string>, K extends string>(
488    obj: T,
489    idFields: K[],
490    idMapping: Partial<Record<T[K], T[K]>>
491): Record<K, string> {
492    for (const idField of idFields) {
493        const newId: T[K] | undefined = idMapping[obj[idField]];
494        if (newId) {
495            obj[idField] = newId;
496        }
497    }
498    return obj;
499}
500
501// Repro from #13285
502
503function updateIds2<T extends { [x: string]: string }, K extends keyof T>(
504    obj: T,
505    key: K,
506    stringMap: { [oldId: string]: string }
507) {
508    var x = obj[key];
509    stringMap[x]; // Should be OK.
510}
511
512// Repro from #13514
513
514declare function head<T extends Array<any>>(list: T): T[0];
515
516// Repro from #13604
517
518class A<T> {
519	props: T & { foo: string };
520}
521
522class B extends A<{ x: number}> {
523	f(p: this["props"]) {
524		p.x;
525	}
526}
527
528// Repro from #13749
529
530class Form<T> {
531    private childFormFactories: {[K in keyof T]: (v: T[K]) => Form<T[K]>}
532
533    public set<K extends keyof T>(prop: K, value: T[K]) {
534        this.childFormFactories[prop](value)
535    }
536}
537
538// Repro from #13787
539
540class SampleClass<P> {
541    public props: Readonly<P>;
542    constructor(props: P) {
543        this.props = Object.freeze(props);
544    }
545}
546
547interface Foo {
548    foo: string;
549}
550
551declare function merge<T, U>(obj1: T, obj2: U): T & U;
552
553class AnotherSampleClass<T> extends SampleClass<T & Foo> {
554    constructor(props: T) {
555        const foo: Foo = { foo: "bar" };
556        super(merge(props, foo));
557    }
558
559    public brokenMethod() {
560        this.props.foo.concat;
561    }
562}
563new AnotherSampleClass({});
564
565// Positive repro from #17166
566function f3<T, K extends Extract<keyof T, string>>(t: T, k: K, tk: T[K]): void {
567    for (let key in t) {
568        key = k // ok, K ==> keyof T
569        t[key] = tk; // ok, T[K] ==> T[keyof T]
570    }
571}
572
573// # 21185
574type Predicates<TaggedRecord> = {
575  [T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T]
576}
577
578// Repros from #23592
579
580type Example<T extends { [K in keyof T]: { prop: any } }> = { [K in keyof T]: T[K]["prop"] };
581type Result = Example<{ a: { prop: string }; b: { prop: number } }>;
582
583type Helper2<T> = { [K in keyof T]: Extract<T[K], { prop: any }> };
584type Example2<T> = { [K in keyof Helper2<T>]: Helper2<T>[K]["prop"] };
585type Result2 = Example2<{ 1: { prop: string }; 2: { prop: number } }>;
586
587// Repro from #23618
588
589type DBBoolTable<K extends string> = { [k in K]: 0 | 1 }
590enum Flag {
591    FLAG_1 = "flag_1",
592    FLAG_2 = "flag_2"
593}
594
595type SimpleDBRecord<Flag extends string> = { staticField: number } & DBBoolTable<Flag>
596function getFlagsFromSimpleRecord<Flag extends string>(record: SimpleDBRecord<Flag>, flags: Flag[]) {
597    return record[flags[0]];
598}
599
600type DynamicDBRecord<Flag extends string> = ({ dynamicField: number } | { dynamicField: string }) & DBBoolTable<Flag>
601function getFlagsFromDynamicRecord<Flag extends string>(record: DynamicDBRecord<Flag>, flags: Flag[]) {
602    return record[flags[0]];
603}
604
605// Repro from #21368
606
607interface I {
608    foo: string;
609}
610
611declare function take<T>(p: T): void;
612
613function fn<T extends I, K extends keyof T>(o: T, k: K) {
614    take<{} | null | undefined>(o[k]);
615    take<any>(o[k]);
616}
617
618// Repro from #23133
619
620class Unbounded<T> {
621    foo(x: T[keyof T]) {
622        let y: {} | undefined | null = x;
623    }
624}
625
626// Repro from #23940
627
628interface I7 {
629    x: any;
630}
631type Foo7<T extends number> = T;
632declare function f7<K extends keyof I7>(type: K): Foo7<I7[K]>;
633
634// Repro from #21770
635
636type Dict<T extends string> = { [key in T]: number };
637type DictDict<V extends string, T extends string> = { [key in V]: Dict<T> };
638
639function ff1<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2: T): number {
640    return dd[k1][k2];
641}
642
643function ff2<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2: T): number {
644    const d: Dict<T> = dd[k1];
645    return d[k2];
646}
647
648// Repro from #26409
649
650const cf1 = <T extends { [P in K]: string; } & { cool: string; }, K extends keyof T>(t: T, k: K) =>
651{
652    const s: string = t[k];
653    t.cool;
654};
655
656const cf2 = <T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) =>
657{
658    const s: string = t[k];
659    t.cool;
660};
661