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