Skip to content

Commit 565ab7a

Browse files
committed
Properly compute lower bound of key type in a mapped type
1 parent 08022d5 commit 565ab7a

File tree

1 file changed

+34
-4
lines changed

1 file changed

+34
-4
lines changed

src/compiler/checker.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7084,6 +7084,39 @@ namespace ts {
70847084
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
70857085
}
70867086

7087+
// Return the lower bound of the key type in a mapped type. Intuitively, the lower
7088+
// bound includes those keys that are known to always be present, for example because
7089+
// because of constraints on type parameters (e.g. 'keyof T' for a constrained T).
7090+
function getLowerBoundOfKeyType(type: Type): Type {
7091+
if (type.flags & (TypeFlags.Any | TypeFlags.Primitive)) {
7092+
return type;
7093+
}
7094+
if (type.flags & TypeFlags.Index) {
7095+
return getIndexType(getApparentType((<IndexType>type).type));
7096+
}
7097+
if (type.flags & TypeFlags.Conditional) {
7098+
return getLowerBoundOfConditionalType(<ConditionalType>type);
7099+
}
7100+
if (type.flags & TypeFlags.Union) {
7101+
return getUnionType(sameMap((<UnionType>type).types, getLowerBoundOfKeyType));
7102+
}
7103+
if (type.flags & TypeFlags.Intersection) {
7104+
return getIntersectionType(sameMap((<UnionType>type).types, getLowerBoundOfKeyType));
7105+
}
7106+
return neverType;
7107+
}
7108+
7109+
function getLowerBoundOfConditionalType(type: ConditionalType) {
7110+
if (type.root.isDistributive) {
7111+
const constraint = getLowerBoundOfKeyType(type.checkType);
7112+
if (constraint !== type.checkType) {
7113+
const mapper = makeUnaryTypeMapper(type.root.checkType, constraint);
7114+
return getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper));
7115+
}
7116+
}
7117+
return type;
7118+
}
7119+
70877120
/** Resolve the members of a mapped type { [P in K]: T } */
70887121
function resolveMappedTypeMembers(type: MappedType) {
70897122
const members: SymbolTable = createSymbolTable();
@@ -7112,10 +7145,7 @@ namespace ts {
71127145
}
71137146
}
71147147
else {
7115-
// If the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
7116-
// Then iterate over the constituents of the key type.
7117-
const iterationType = constraintType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>constraintType).type)) : constraintType;
7118-
forEachType(iterationType, addMemberForKeyType);
7148+
forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType);
71197149
}
71207150
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
71217151

0 commit comments

Comments
 (0)