Skip to content

Commit 702efd5

Browse files
authored
Merge pull request #12026 from Microsoft/keyofAndConstraints
Fix 'keyof' for constrained type parameters
2 parents 9705791 + 48f2b78 commit 702efd5

File tree

6 files changed

+762
-291
lines changed

6 files changed

+762
-291
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6825,6 +6825,27 @@ namespace ts {
68256825
}
68266826
}
68276827

6828+
if (target.flags & TypeFlags.TypeParameter) {
6829+
// Given a type parameter K with a constraint keyof T, a type S is
6830+
// assignable to K if S is assignable to keyof T.
6831+
const constraint = getConstraintOfTypeParameter(<TypeParameter>target);
6832+
if (constraint && constraint.flags & TypeFlags.Index) {
6833+
if (result = isRelatedTo(source, constraint, reportErrors)) {
6834+
return result;
6835+
}
6836+
}
6837+
}
6838+
else if (target.flags & TypeFlags.Index) {
6839+
// Given a type parameter T with a constraint C, a type S is assignable to
6840+
// keyof T if S is assignable to keyof C.
6841+
const constraint = getConstraintOfTypeParameter((<IndexType>target).type);
6842+
if (constraint) {
6843+
if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
6844+
return result;
6845+
}
6846+
}
6847+
}
6848+
68286849
if (source.flags & TypeFlags.TypeParameter) {
68296850
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
68306851

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2709,7 +2709,7 @@ namespace ts {
27092709
EnumLike = Enum | EnumLiteral,
27102710
UnionOrIntersection = Union | Intersection,
27112711
StructuredType = Object | Union | Intersection,
2712-
StructuredOrTypeParameter = StructuredType | TypeParameter,
2712+
StructuredOrTypeParameter = StructuredType | TypeParameter | Index,
27132713

27142714
// 'Narrowable' types are types where narrowing actually narrows.
27152715
// This *should* be every type other than null, undefined, void, and never

tests/baselines/reference/keyofAndIndexedAccess.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ class Shape {
77
visible: boolean;
88
}
99

10+
class TaggedShape extends Shape {
11+
tag: string;
12+
}
13+
1014
class Item {
1115
name: string;
1216
price: number;
@@ -149,6 +153,17 @@ function f32<K extends "width" | "height">(key: K) {
149153
return shape[key]; // Shape[K]
150154
}
151155

156+
function f33<S extends Shape, K extends keyof S>(shape: S, key: K) {
157+
let name = getProperty(shape, "name");
158+
let prop = getProperty(shape, key);
159+
return prop;
160+
}
161+
162+
function f34(ts: TaggedShape) {
163+
let tag1 = f33(ts, "tag");
164+
let tag2 = getProperty(ts, "tag");
165+
}
166+
152167
class C {
153168
public x: string;
154169
protected y: string;
@@ -164,14 +179,58 @@ function f40(c: C) {
164179
let x: X = c["x"];
165180
let y: Y = c["y"];
166181
let z: Z = c["z"];
182+
}
183+
184+
// Repros from #12011
185+
186+
class Base {
187+
get<K extends keyof this>(prop: K) {
188+
return this[prop];
189+
}
190+
set<K extends keyof this>(prop: K, value: this[K]) {
191+
this[prop] = value;
192+
}
193+
}
194+
195+
class Person extends Base {
196+
parts: number;
197+
constructor(parts: number) {
198+
super();
199+
this.set("parts", parts);
200+
}
201+
getParts() {
202+
return this.get("parts")
203+
}
204+
}
205+
206+
class OtherPerson {
207+
parts: number;
208+
constructor(parts: number) {
209+
setProperty(this, "parts", parts);
210+
}
211+
getParts() {
212+
return getProperty(this, "parts")
213+
}
167214
}
168215

169216
//// [keyofAndIndexedAccess.js]
217+
var __extends = (this && this.__extends) || function (d, b) {
218+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
219+
function __() { this.constructor = d; }
220+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
221+
};
170222
var Shape = (function () {
171223
function Shape() {
172224
}
173225
return Shape;
174226
}());
227+
var TaggedShape = (function (_super) {
228+
__extends(TaggedShape, _super);
229+
function TaggedShape() {
230+
return _super.apply(this, arguments) || this;
231+
}
232+
return TaggedShape;
233+
}(Shape));
175234
var Item = (function () {
176235
function Item() {
177236
}
@@ -249,6 +308,15 @@ function f32(key) {
249308
var shape = { name: "foo", width: 5, height: 10, visible: true };
250309
return shape[key]; // Shape[K]
251310
}
311+
function f33(shape, key) {
312+
var name = getProperty(shape, "name");
313+
var prop = getProperty(shape, key);
314+
return prop;
315+
}
316+
function f34(ts) {
317+
var tag1 = f33(ts, "tag");
318+
var tag2 = getProperty(ts, "tag");
319+
}
252320
var C = (function () {
253321
function C() {
254322
}
@@ -261,6 +329,39 @@ function f40(c) {
261329
var y = c["y"];
262330
var z = c["z"];
263331
}
332+
// Repros from #12011
333+
var Base = (function () {
334+
function Base() {
335+
}
336+
Base.prototype.get = function (prop) {
337+
return this[prop];
338+
};
339+
Base.prototype.set = function (prop, value) {
340+
this[prop] = value;
341+
};
342+
return Base;
343+
}());
344+
var Person = (function (_super) {
345+
__extends(Person, _super);
346+
function Person(parts) {
347+
var _this = _super.call(this) || this;
348+
_this.set("parts", parts);
349+
return _this;
350+
}
351+
Person.prototype.getParts = function () {
352+
return this.get("parts");
353+
};
354+
return Person;
355+
}(Base));
356+
var OtherPerson = (function () {
357+
function OtherPerson(parts) {
358+
setProperty(this, "parts", parts);
359+
}
360+
OtherPerson.prototype.getParts = function () {
361+
return getProperty(this, "parts");
362+
};
363+
return OtherPerson;
364+
}());
264365

265366

266367
//// [keyofAndIndexedAccess.d.ts]
@@ -270,6 +371,9 @@ declare class Shape {
270371
height: number;
271372
visible: boolean;
272373
}
374+
declare class TaggedShape extends Shape {
375+
tag: string;
376+
}
273377
declare class Item {
274378
name: string;
275379
price: number;
@@ -342,9 +446,25 @@ declare function pluck<T, K extends keyof T>(array: T[], key: K): T[K][];
342446
declare function f30(shapes: Shape[]): void;
343447
declare function f31<K extends keyof Shape>(key: K): Shape[K];
344448
declare function f32<K extends "width" | "height">(key: K): Shape[K];
449+
declare function f33<S extends Shape, K extends keyof S>(shape: S, key: K): S[K];
450+
declare function f34(ts: TaggedShape): void;
345451
declare class C {
346452
x: string;
347453
protected y: string;
348454
private z;
349455
}
350456
declare function f40(c: C): void;
457+
declare class Base {
458+
get<K extends keyof this>(prop: K): this[K];
459+
set<K extends keyof this>(prop: K, value: this[K]): void;
460+
}
461+
declare class Person extends Base {
462+
parts: number;
463+
constructor(parts: number);
464+
getParts(): number;
465+
}
466+
declare class OtherPerson {
467+
parts: number;
468+
constructor(parts: number);
469+
getParts(): number;
470+
}

0 commit comments

Comments
 (0)