// @strictNullChecks: true // @declaration: true class Shape { name: string; width: number; height: number; visible: boolean; } class TaggedShape extends Shape { tag: string; } class Item { name: string; price: number; } class Options { visible: "yes" | "no"; } type Dictionary = { [x: string]: T }; type NumericallyIndexed = { [x: number]: T }; const enum E { A, B, C } type K00 = keyof any; // string type K01 = keyof string; // "toString" | "charAt" | ... type K02 = keyof number; // "toString" | "toFixed" | "toExponential" | ... type K03 = keyof boolean; // "valueOf" type K04 = keyof void; // never type K05 = keyof undefined; // never type K06 = keyof null; // never type K07 = keyof never; // string | number | symbol type K08 = keyof unknown; // never type K10 = keyof Shape; // "name" | "width" | "height" | "visible" type K11 = keyof Shape[]; // "length" | "toString" | ... type K12 = keyof Dictionary; // string type K13 = keyof {}; // never type K14 = keyof Object; // "constructor" | "toString" | ... type K15 = keyof E; // "toString" | "toFixed" | "toExponential" | ... type K16 = keyof [string, number]; // "0" | "1" | "length" | "toString" | ... type K17 = keyof (Shape | Item); // "name" type K18 = keyof (Shape & Item); // "name" | "width" | "height" | "visible" | "price" type K19 = keyof NumericallyIndexed // never type KeyOf = keyof T; type K20 = KeyOf; // "name" | "width" | "height" | "visible" type K21 = KeyOf>; // string type NAME = "name"; type WIDTH_OR_HEIGHT = "width" | "height"; type Q10 = Shape["name"]; // string type Q11 = Shape["width" | "height"]; // number type Q12 = Shape["name" | "visible"]; // string | boolean type Q20 = Shape[NAME]; // string type Q21 = Shape[WIDTH_OR_HEIGHT]; // number type Q30 = [string, number][0]; // string type Q31 = [string, number][1]; // number type Q32 = [string, number][number]; // string | number type Q33 = [string, number][E.A]; // string type Q34 = [string, number][E.B]; // number type Q35 = [string, number]["0"]; // string type Q36 = [string, number]["1"]; // string type Q40 = (Shape | Options)["visible"]; // boolean | "yes" | "no" type Q41 = (Shape & Options)["visible"]; // true & "yes" | true & "no" | false & "yes" | false & "no" type Q50 = Dictionary["howdy"]; // Shape type Q51 = Dictionary[123]; // Shape type Q52 = Dictionary[E.B]; // Shape declare let cond: boolean; function getProperty(obj: T, key: K) { return obj[key]; } function setProperty(obj: T, key: K, value: T[K]) { obj[key] = value; } function f10(shape: Shape) { let name = getProperty(shape, "name"); // string let widthOrHeight = getProperty(shape, cond ? "width" : "height"); // number let nameOrVisible = getProperty(shape, cond ? "name" : "visible"); // string | boolean setProperty(shape, "name", "rectangle"); setProperty(shape, cond ? "width" : "height", 10); setProperty(shape, cond ? "name" : "visible", true); // Technically not safe } function f11(a: Shape[]) { let len = getProperty(a, "length"); // number setProperty(a, "length", len); } function f12(t: [Shape, boolean]) { let len = getProperty(t, "length"); let s2 = getProperty(t, "0"); // Shape let b2 = getProperty(t, "1"); // boolean } function f13(foo: any, bar: any) { let x = getProperty(foo, "x"); // any let y = getProperty(foo, "100"); // any let z = getProperty(foo, bar); // any } class Component { props: PropType; getProperty(key: K) { return this.props[key]; } setProperty(key: K, value: PropType[K]) { this.props[key] = value; } } function f20(component: Component) { let name = component.getProperty("name"); // string let widthOrHeight = component.getProperty(cond ? "width" : "height"); // number let nameOrVisible = component.getProperty(cond ? "name" : "visible"); // string | boolean component.setProperty("name", "rectangle"); component.setProperty(cond ? "width" : "height", 10) component.setProperty(cond ? "name" : "visible", true); // Technically not safe } function pluck(array: T[], key: K) { return array.map(x => x[key]); } function f30(shapes: Shape[]) { let names = pluck(shapes, "name"); // string[] let widths = pluck(shapes, "width"); // number[] let nameOrVisibles = pluck(shapes, cond ? "name" : "visible"); // (string | boolean)[] } function f31(key: K) { const shape: Shape = { name: "foo", width: 5, height: 10, visible: true }; return shape[key]; // Shape[K] } function f32(key: K) { const shape: Shape = { name: "foo", width: 5, height: 10, visible: true }; return shape[key]; // Shape[K] } function f33(shape: S, key: K) { let name = getProperty(shape, "name"); let prop = getProperty(shape, key); return prop; } function f34(ts: TaggedShape) { let tag1 = f33(ts, "tag"); let tag2 = getProperty(ts, "tag"); } class C { public x: string; protected y: string; private z: string; } // Indexed access expressions have always permitted access to private and protected members. // For consistency we also permit such access in indexed access types. function f40(c: C) { type X = C["x"]; type Y = C["y"]; type Z = C["z"]; let x: X = c["x"]; let y: Y = c["y"]; let z: Z = c["z"]; } function f50(k: keyof T, s: string) { const x1 = s as keyof T; const x2 = k as string; } function f51(k: K, s: string) { const x1 = s as keyof T; const x2 = k as string; } function f52(obj: { [x: string]: boolean }, k: Exclude, s: string, n: number) { const x1 = obj[s]; const x2 = obj[n]; const x3 = obj[k]; } function f53>(obj: { [x: string]: boolean }, k: K, s: string, n: number) { const x1 = obj[s]; const x2 = obj[n]; const x3 = obj[k]; } function f54(obj: T, key: keyof T) { for (let s in obj[key]) { } const b = "foo" in obj[key]; } function f55(obj: T, key: K) { for (let s in obj[key]) { } const b = "foo" in obj[key]; } function f60(source: T, target: T) { for (let k in source) { target[k] = source[k]; } } function f70(func: (k1: keyof (T | U), k2: keyof (T & U)) => void) { func<{ a: any, b: any }, { a: any, c: any }>('a', 'a'); func<{ a: any, b: any }, { a: any, c: any }>('a', 'b'); func<{ a: any, b: any }, { a: any, c: any }>('a', 'c'); } function f71(func: (x: T, y: U) => Partial) { let x = func({ a: 1, b: "hello" }, { c: true }); x.a; // number | undefined x.b; // string | undefined x.c; // boolean | undefined } function f72(func: (x: T, y: U, k: K) => (T & U)[K]) { let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean } function f73(func: (x: T, y: U, k: K) => (T & U)[K]) { let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean } function f74(func: (x: T, y: U, k: K) => (T | U)[K]) { let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean } function f80(obj: T) { let a1 = obj.a; // { x: any } let a2 = obj['a']; // { x: any } let a3 = obj['a'] as T['a']; // T["a"] let x1 = obj.a.x; // any let x2 = obj['a']['x']; // any let x3 = obj['a']['x'] as T['a']['x']; // T["a"]["x"] } function f81(obj: T) { return obj['a']['x'] as T['a']['x']; } function f82() { let x1 = f81({ a: { x: "hello" } }); // string let x2 = f81({ a: { x: 42 } }); // number } function f83(obj: T, key: K) { return obj[key]['x'] as T[K]['x']; } function f84() { let x1 = f83({ foo: { x: "hello" } }, "foo"); // string let x2 = f83({ bar: { x: 42 } }, "bar"); // number } class C1 { x: number; get(key: K) { return this[key]; } set(key: K, value: this[K]) { this[key] = value; } foo() { let x1 = this.x; // number let x2 = this["x"]; // number let x3 = this.get("x"); // this["x"] let x4 = getProperty(this, "x"); // this["x"] this.x = 42; this["x"] = 42; this.set("x", 42); setProperty(this, "x", 42); } } type S2 = { a: string; b: string; }; function f90(x1: S2[keyof S2], x2: T[keyof S2], x3: S2[K]) { x1 = x2; x1 = x3; x2 = x1; x2 = x3; x3 = x1; x3 = x2; x1.length; x2.length; x3.length; } function f91(x: T, y: T[keyof T], z: T[K]) { let a: {}; a = x; a = y; a = z; } function f92(x: T, y: T[keyof T], z: T[K]) { let a: {} | null | undefined; a = x; a = y; a = z; } // Repros from #12011 class Base { get(prop: K) { return this[prop]; } set(prop: K, value: this[K]) { this[prop] = value; } } class Person extends Base { parts: number; constructor(parts: number) { super(); this.set("parts", parts); } getParts() { return this.get("parts") } } class OtherPerson { parts: number; constructor(parts: number) { setProperty(this, "parts", parts); } getParts() { return getProperty(this, "parts") } } // Modified repro from #12544 function path(obj: T, key1: K1): T[K1]; function path(obj: T, key1: K1, key2: K2): T[K1][K2]; function path(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; function path(obj: any, ...keys: (string | number)[]): any; function path(obj: any, ...keys: (string | number)[]): any { let result = obj; for (let k of keys) { result = result[k]; } return result; } type Thing = { a: { x: number, y: string }, b: boolean }; function f1(thing: Thing) { let x1 = path(thing, 'a'); // { x: number, y: string } let x2 = path(thing, 'a', 'y'); // string let x3 = path(thing, 'b'); // boolean let x4 = path(thing, ...['a', 'x']); // any } // Repro from comment in #12114 const assignTo2 = (object: T, key1: K1, key2: K2) => (value: T[K1][K2]) => object[key1][key2] = value; // Modified repro from #12573 declare function one(handler: (t: T) => void): T var empty = one(() => {}) // inferred as {}, expected type Handlers = { [K in keyof T]: (t: T[K]) => void } declare function on(handlerHash: Handlers): T var hashOfEmpty1 = on({ test: () => {} }); // {} var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } // Repro from #12624 interface Options1 { data?: Data computed?: Computed; } declare class Component1 { constructor(options: Options1); get(key: K): (Data & Computed)[K]; } let c1 = new Component1({ data: { hello: "" } }); c1.get("hello"); // Repro from #12625 interface Options2 { data?: Data computed?: Computed; } declare class Component2 { constructor(options: Options2); get(key: K): (Data & Computed)[K]; } // Repro from #12641 interface R { p: number; } function f(p: K) { let a: any; a[p].add; // any } // Repro from #12651 type MethodDescriptor = { name: string; args: any[]; returnValue: any; } declare function dispatchMethod(name: M['name'], args: M['args']): M['returnValue']; type SomeMethodDescriptor = { name: "someMethod"; args: [string, number]; returnValue: string[]; } let result = dispatchMethod("someMethod", ["hello", 35]); // Repro from #13073 type KeyTypes = "a" | "b" let MyThingy: { [key in KeyTypes]: string[] }; function addToMyThingy(key: S) { MyThingy[key].push("a"); } // Repro from #13102 type Handler = { onChange: (name: keyof T) => void; }; function onChangeGenericFunction(handler: Handler) { handler.onChange('preset') } // Repro from #13285 function updateIds, K extends string>( obj: T, idFields: K[], idMapping: Partial> ): Record { for (const idField of idFields) { const newId: T[K] | undefined = idMapping[obj[idField]]; if (newId) { obj[idField] = newId; } } return obj; } // Repro from #13285 function updateIds2( obj: T, key: K, stringMap: { [oldId: string]: string } ) { var x = obj[key]; stringMap[x]; // Should be OK. } // Repro from #13514 declare function head>(list: T): T[0]; // Repro from #13604 class A { props: T & { foo: string }; } class B extends A<{ x: number}> { f(p: this["props"]) { p.x; } } // Repro from #13749 class Form { private childFormFactories: {[K in keyof T]: (v: T[K]) => Form} public set(prop: K, value: T[K]) { this.childFormFactories[prop](value) } } // Repro from #13787 class SampleClass

{ public props: Readonly

; constructor(props: P) { this.props = Object.freeze(props); } } interface Foo { foo: string; } declare function merge(obj1: T, obj2: U): T & U; class AnotherSampleClass extends SampleClass { constructor(props: T) { const foo: Foo = { foo: "bar" }; super(merge(props, foo)); } public brokenMethod() { this.props.foo.concat; } } new AnotherSampleClass({}); // Positive repro from #17166 function f3>(t: T, k: K, tk: T[K]): void { for (let key in t) { key = k // ok, K ==> keyof T t[key] = tk; // ok, T[K] ==> T[keyof T] } } // # 21185 type Predicates = { [T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T] } // Repros from #23592 type Example = { [K in keyof T]: T[K]["prop"] }; type Result = Example<{ a: { prop: string }; b: { prop: number } }>; type Helper2 = { [K in keyof T]: Extract }; type Example2 = { [K in keyof Helper2]: Helper2[K]["prop"] }; type Result2 = Example2<{ 1: { prop: string }; 2: { prop: number } }>; // Repro from #23618 type DBBoolTable = { [k in K]: 0 | 1 } enum Flag { FLAG_1 = "flag_1", FLAG_2 = "flag_2" } type SimpleDBRecord = { staticField: number } & DBBoolTable function getFlagsFromSimpleRecord(record: SimpleDBRecord, flags: Flag[]) { return record[flags[0]]; } type DynamicDBRecord = ({ dynamicField: number } | { dynamicField: string }) & DBBoolTable function getFlagsFromDynamicRecord(record: DynamicDBRecord, flags: Flag[]) { return record[flags[0]]; } // Repro from #21368 interface I { foo: string; } declare function take(p: T): void; function fn(o: T, k: K) { take<{} | null | undefined>(o[k]); take(o[k]); } // Repro from #23133 class Unbounded { foo(x: T[keyof T]) { let y: {} | undefined | null = x; } } // Repro from #23940 interface I7 { x: any; } type Foo7 = T; declare function f7(type: K): Foo7; // Repro from #21770 type Dict = { [key in T]: number }; type DictDict = { [key in V]: Dict }; function ff1(dd: DictDict, k1: V, k2: T): number { return dd[k1][k2]; } function ff2(dd: DictDict, k1: V, k2: T): number { const d: Dict = dd[k1]; return d[k2]; } // Repro from #26409 const cf1 = (t: T, k: K) => { const s: string = t[k]; t.cool; }; const cf2 = (t: T, k: K) => { const s: string = t[k]; t.cool; };