Skip to content

Commit dcd225a

Browse files
committed
Fix comparable relation for keyof T
Treat keyof T as string | number for purposes of indexing Allow indexed access types with for-in and in operator
1 parent e81da9c commit dcd225a

File tree

1 file changed

+12
-8
lines changed

1 file changed

+12
-8
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6037,19 +6037,20 @@ namespace ts {
60376037
const id = objectType.id + "," + indexType.id;
60386038
return indexedAccessTypes[id] || (indexedAccessTypes[id] = createIndexedAccessType(objectType, indexType));
60396039
}
6040-
const apparentType = getApparentType(objectType);
6041-
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
6040+
const apparentObjectType = getApparentType(objectType);
6041+
const apparentIndexType = indexType.flags & TypeFlags.Index ? stringOrNumberType : indexType;
6042+
if (apparentIndexType.flags & TypeFlags.Union && !(apparentIndexType.flags & TypeFlags.Primitive)) {
60426043
const propTypes: Type[] = [];
6043-
for (const t of (<UnionType>indexType).types) {
6044-
const propType = getPropertyTypeForIndexType(apparentType, t, accessNode, /*cacheSymbol*/ false);
6044+
for (const t of (<UnionType>apparentIndexType).types) {
6045+
const propType = getPropertyTypeForIndexType(apparentObjectType, t, accessNode, /*cacheSymbol*/ false);
60456046
if (propType === unknownType) {
60466047
return unknownType;
60476048
}
60486049
propTypes.push(propType);
60496050
}
60506051
return getUnionType(propTypes);
60516052
}
6052-
return getPropertyTypeForIndexType(apparentType, indexType, accessNode, /*cacheSymbol*/ true);
6053+
return getPropertyTypeForIndexType(apparentObjectType, apparentIndexType, accessNode, /*cacheSymbol*/ true);
60536054
}
60546055

60556056
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
@@ -7123,7 +7124,10 @@ namespace ts {
71237124

71247125
if (source.flags & TypeFlags.Index) {
71257126
// A keyof T is related to a union type containing both string and number
7126-
if (maybeTypeOfKind(target, TypeFlags.String) && maybeTypeOfKind(target, TypeFlags.Number)) {
7127+
const related = relation === comparableRelation ?
7128+
maybeTypeOfKind(target, TypeFlags.String | TypeFlags.Number) :
7129+
maybeTypeOfKind(target, TypeFlags.String) && maybeTypeOfKind(target, TypeFlags.Number);
7130+
if (related) {
71277131
return Ternary.True;
71287132
}
71297133
}
@@ -14254,7 +14258,7 @@ namespace ts {
1425414258
if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
1425514259
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
1425614260
}
14257-
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter)) {
14261+
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
1425814262
error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
1425914263
}
1426014264
return booleanType;
@@ -17148,7 +17152,7 @@ namespace ts {
1714817152
const rightType = checkNonNullExpression(node.expression);
1714917153
// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
1715017154
// in this case error about missing name is already reported - do not report extra one
17151-
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter)) {
17155+
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
1715217156
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);
1715317157
}
1715417158

0 commit comments

Comments
 (0)