Skip to content

Commit f58e1a2

Browse files
committed
Map both declared properties and string index signatures
1 parent 2e97cff commit f58e1a2

File tree

1 file changed

+20
-9
lines changed

1 file changed

+20
-9
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4536,15 +4536,27 @@ namespace ts {
45364536
const typeParameter = getTypeParameterFromMappedType(type);
45374537
const constraintType = getConstraintTypeFromMappedType(type);
45384538
const templateType = getTemplateTypeFromMappedType(type);
4539-
const modifiersType = getModifiersTypeFromMappedType(type);
4539+
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type));
45404540
const templateReadonly = !!type.declaration.readonlyToken;
45414541
const templateOptional = !!type.declaration.questionToken;
4542-
// First, if the constraint type is a type parameter, obtain the base constraint. Then,
4543-
// if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
4544-
// Finally, iterate over the constituents of the resulting iteration type.
4545-
const keyType = constraintType.flags & TypeFlags.TypeVariable ? getApparentType(constraintType) : constraintType;
4546-
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
4547-
forEachType(iterationType, t => {
4542+
if (type.declaration.typeParameter.constraint.kind === SyntaxKind.TypeOperator) {
4543+
// We have a { [P in keyof T]: X }
4544+
forEachType(getLiteralTypeFromPropertyNames(modifiersType), addMemberForKeyType);
4545+
if (getIndexInfoOfType(modifiersType, IndexKind.String)) {
4546+
addMemberForKeyType(stringType);
4547+
}
4548+
}
4549+
else {
4550+
// First, if the constraint type is a type parameter, obtain the base constraint. Then,
4551+
// if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
4552+
// Finally, iterate over the constituents of the resulting iteration type.
4553+
const keyType = constraintType.flags & TypeFlags.TypeVariable ? getApparentType(constraintType) : constraintType;
4554+
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
4555+
forEachType(iterationType, addMemberForKeyType);
4556+
}
4557+
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
4558+
4559+
function addMemberForKeyType(t: Type) {
45484560
// Create a mapper from T to the current iteration type constituent. Then, if the
45494561
// mapped type is itself an instantiated type, combine the iteration mapper with the
45504562
// instantiation mapper.
@@ -4565,8 +4577,7 @@ namespace ts {
45654577
else if (t.flags & TypeFlags.String) {
45664578
stringIndexInfo = createIndexInfo(propType, templateReadonly);
45674579
}
4568-
});
4569-
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
4580+
}
45704581
}
45714582

45724583
function getTypeParameterFromMappedType(type: MappedType) {

0 commit comments

Comments
 (0)