Skip to content

Commit 998246a

Browse files
authored
Merge pull request #12389 from Microsoft/keyofRelations
Minor fixes for 'keyof T' and indexed acces types
2 parents f8b8465 + 4ce494a commit 998246a

File tree

5 files changed

+512
-44
lines changed

5 files changed

+512
-44
lines changed

src/compiler/checker.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -6039,19 +6039,20 @@ namespace ts {
60396039
const id = objectType.id + "," + indexType.id;
60406040
return indexedAccessTypes[id] || (indexedAccessTypes[id] = createIndexedAccessType(objectType, indexType));
60416041
}
6042-
const apparentType = getApparentType(objectType);
6043-
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
6042+
const apparentObjectType = getApparentType(objectType);
6043+
const apparentIndexType = indexType.flags & TypeFlags.Index ? stringOrNumberType : indexType;
6044+
if (apparentIndexType.flags & TypeFlags.Union && !(apparentIndexType.flags & TypeFlags.Primitive)) {
60446045
const propTypes: Type[] = [];
6045-
for (const t of (<UnionType>indexType).types) {
6046-
const propType = getPropertyTypeForIndexType(apparentType, t, accessNode, /*cacheSymbol*/ false);
6046+
for (const t of (<UnionType>apparentIndexType).types) {
6047+
const propType = getPropertyTypeForIndexType(apparentObjectType, t, accessNode, /*cacheSymbol*/ false);
60476048
if (propType === unknownType) {
60486049
return unknownType;
60496050
}
60506051
propTypes.push(propType);
60516052
}
60526053
return getUnionType(propTypes);
60536054
}
6054-
return getPropertyTypeForIndexType(apparentType, indexType, accessNode, /*cacheSymbol*/ true);
6055+
return getPropertyTypeForIndexType(apparentObjectType, apparentIndexType, accessNode, /*cacheSymbol*/ true);
60556056
}
60566057

60576058
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
@@ -7125,7 +7126,10 @@ namespace ts {
71257126

71267127
if (source.flags & TypeFlags.Index) {
71277128
// A keyof T is related to a union type containing both string and number
7128-
if (maybeTypeOfKind(target, TypeFlags.String) && maybeTypeOfKind(target, TypeFlags.Number)) {
7129+
const related = relation === comparableRelation ?
7130+
maybeTypeOfKind(target, TypeFlags.String | TypeFlags.Number) :
7131+
maybeTypeOfKind(target, TypeFlags.String) && maybeTypeOfKind(target, TypeFlags.Number);
7132+
if (related) {
71297133
return Ternary.True;
71307134
}
71317135
}
@@ -14256,7 +14260,7 @@ namespace ts {
1425614260
if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
1425714261
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
1425814262
}
14259-
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter)) {
14263+
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
1426014264
error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
1426114265
}
1426214266
return booleanType;
@@ -17161,7 +17165,7 @@ namespace ts {
1716117165
const rightType = checkNonNullExpression(node.expression);
1716217166
// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
1716317167
// in this case error about missing name is already reported - do not report extra one
17164-
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter)) {
17168+
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
1716517169
error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter);
1716617170
}
1716717171

tests/baselines/reference/keyofAndIndexedAccess.js

+84
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,46 @@ function f40(c: C) {
181181
let z: Z = c["z"];
182182
}
183183

184+
function f50<T>(k: keyof T, s: string, n: number) {
185+
const x1 = s as keyof T;
186+
const x2 = n as keyof T;
187+
const x3 = k as string;
188+
const x4 = k as number;
189+
const x5 = k as string | number;
190+
}
191+
192+
function f51<T, K extends keyof T>(k: K, s: string, n: number) {
193+
const x1 = s as keyof T;
194+
const x2 = n as keyof T;
195+
const x3 = k as string;
196+
const x4 = k as number;
197+
const x5 = k as string | number;
198+
}
199+
200+
function f52<T>(obj: { [x: string]: boolean }, k: keyof T, s: string, n: number) {
201+
const x1 = obj[s];
202+
const x2 = obj[n];
203+
const x3 = obj[k];
204+
}
205+
206+
function f53<T, K extends keyof T>(obj: { [x: string]: boolean }, k: K, s: string, n: number) {
207+
const x1 = obj[s];
208+
const x2 = obj[n];
209+
const x3 = obj[k];
210+
}
211+
212+
function f54<T>(obj: T, key: keyof T) {
213+
for (let s in obj[key]) {
214+
}
215+
const b = "foo" in obj[key];
216+
}
217+
218+
function f55<T, K extends keyof T>(obj: T, key: K) {
219+
for (let s in obj[key]) {
220+
}
221+
const b = "foo" in obj[key];
222+
}
223+
184224
// Repros from #12011
185225

186226
class Base {
@@ -329,6 +369,40 @@ function f40(c) {
329369
var y = c["y"];
330370
var z = c["z"];
331371
}
372+
function f50(k, s, n) {
373+
var x1 = s;
374+
var x2 = n;
375+
var x3 = k;
376+
var x4 = k;
377+
var x5 = k;
378+
}
379+
function f51(k, s, n) {
380+
var x1 = s;
381+
var x2 = n;
382+
var x3 = k;
383+
var x4 = k;
384+
var x5 = k;
385+
}
386+
function f52(obj, k, s, n) {
387+
var x1 = obj[s];
388+
var x2 = obj[n];
389+
var x3 = obj[k];
390+
}
391+
function f53(obj, k, s, n) {
392+
var x1 = obj[s];
393+
var x2 = obj[n];
394+
var x3 = obj[k];
395+
}
396+
function f54(obj, key) {
397+
for (var s in obj[key]) {
398+
}
399+
var b = "foo" in obj[key];
400+
}
401+
function f55(obj, key) {
402+
for (var s in obj[key]) {
403+
}
404+
var b = "foo" in obj[key];
405+
}
332406
// Repros from #12011
333407
var Base = (function () {
334408
function Base() {
@@ -454,6 +528,16 @@ declare class C {
454528
private z;
455529
}
456530
declare function f40(c: C): void;
531+
declare function f50<T>(k: keyof T, s: string, n: number): void;
532+
declare function f51<T, K extends keyof T>(k: K, s: string, n: number): void;
533+
declare function f52<T>(obj: {
534+
[x: string]: boolean;
535+
}, k: keyof T, s: string, n: number): void;
536+
declare function f53<T, K extends keyof T>(obj: {
537+
[x: string]: boolean;
538+
}, k: K, s: string, n: number): void;
539+
declare function f54<T>(obj: T, key: keyof T): void;
540+
declare function f55<T, K extends keyof T>(obj: T, key: K): void;
457541
declare class Base {
458542
get<K extends keyof this>(prop: K): this[K];
459543
set<K extends keyof this>(prop: K, value: this[K]): void;

0 commit comments

Comments
 (0)