@@ -4497,12 +4497,14 @@ namespace ts {
4497
4497
// Resolve upfront such that recursive references see an empty object type.
4498
4498
setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
4499
4499
// In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type,
4500
- // and T as the template type.
4500
+ // and T as the template type. If K is of the form 'keyof S', the mapped type and S are
4501
+ // isomorphic and we copy property modifiers from corresponding properties in S.
4501
4502
const typeParameter = getTypeParameterFromMappedType(type);
4502
4503
const constraintType = getConstraintTypeFromMappedType(type);
4504
+ const isomorphicType = getIsomorphicTypeFromMappedType(type);
4503
4505
const templateType = getTemplateTypeFromMappedType(type);
4504
- const isReadonly = !!type.declaration.readonlyToken;
4505
- const isOptional = !!type.declaration.questionToken;
4506
+ const templateReadonly = !!type.declaration.readonlyToken;
4507
+ const templateOptional = !!type.declaration.questionToken;
4506
4508
// First, if the constraint type is a type parameter, obtain the base constraint. Then,
4507
4509
// if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
4508
4510
// Finally, iterate over the constituents of the resulting iteration type.
@@ -4515,18 +4517,19 @@ namespace ts {
4515
4517
const iterationMapper = createUnaryTypeMapper(typeParameter, t);
4516
4518
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
4517
4519
const propType = instantiateType(templateType, templateMapper);
4518
- // If the current iteration type constituent is a literal type, create a property.
4519
- // Otherwise, for type string create a string index signature and for type number
4520
- // create a numeric index signature.
4521
- if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) {
4520
+ // If the current iteration type constituent is a string literal type, create a property.
4521
+ // Otherwise, for type string create a string index signature.
4522
+ if (t.flags & TypeFlags.StringLiteral) {
4522
4523
const propName = (<LiteralType>t).text;
4524
+ const isomorphicProp = isomorphicType && getPropertyOfType(isomorphicType, propName);
4525
+ const isOptional = templateOptional || !!(isomorphicProp && isomorphicProp.flags & SymbolFlags.Optional);
4523
4526
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName);
4524
4527
prop.type = addOptionality(propType, isOptional);
4525
- prop.isReadonly = isReadonly ;
4528
+ prop.isReadonly = templateReadonly || isomorphicProp && isReadonlySymbol(isomorphicProp) ;
4526
4529
members[propName] = prop;
4527
4530
}
4528
4531
else if (t.flags & TypeFlags.String) {
4529
- stringIndexInfo = createIndexInfo(propType, isReadonly );
4532
+ stringIndexInfo = createIndexInfo(propType, templateReadonly );
4530
4533
}
4531
4534
});
4532
4535
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
@@ -4549,6 +4552,11 @@ namespace ts {
4549
4552
unknownType);
4550
4553
}
4551
4554
4555
+ function getIsomorphicTypeFromMappedType(type: MappedType) {
4556
+ const constraint = getConstraintDeclaration(getTypeParameterFromMappedType(type));
4557
+ return constraint.kind === SyntaxKind.TypeOperator ? instantiateType(getTypeFromTypeNode((<TypeOperatorNode>constraint).type), type.mapper || identityMapper) : undefined;
4558
+ }
4559
+
4552
4560
function getErasedTemplateTypeFromMappedType(type: MappedType) {
4553
4561
return instantiateType(getTemplateTypeFromMappedType(type), createUnaryTypeMapper(getTypeParameterFromMappedType(type), anyType));
4554
4562
}
0 commit comments