@@ -4484,33 +4484,33 @@ namespace ts {
4484
4484
}
4485
4485
}
4486
4486
4487
- function forEachType<T>(type: Type, f: (t: Type) => T): T {
4488
- return type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, f) : f(type);
4489
- }
4490
-
4491
- // { [P in K]: T }
4492
- // Get apparent type of K
4493
- // If apparent type is a 'keyof T', get apparent type of T
4494
- // For each constituent literal type U
4495
- // create mapper from P to U
4496
- // instantiate T using mapper
4497
- // if U is string or number, create index signature with instantiated type
4498
- // otherwise create property with name from U and instantiated type
4487
+ /** Resolve the members of a mapped type { [P in K]: T } */
4499
4488
function resolveMappedTypeMembers(type: MappedType) {
4500
4489
const members: SymbolTable = createMap<Symbol>();
4501
4490
let stringIndexInfo: IndexInfo;
4502
4491
let numberIndexInfo: IndexInfo;
4492
+ // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type,
4493
+ // and T as the template type.
4503
4494
const typeParameter = getTypeParameterFromMappedType(type);
4504
4495
const constraintType = getConstraintTypeFromMappedType(type);
4505
4496
const templateType = getTemplateTypeFromMappedType(type);
4506
4497
const isReadonly = !!type.declaration.readonlyToken;
4507
4498
const isOptional = !!type.declaration.questionToken;
4499
+ // First, if the constraint type is a type parameter, obtain the base constraint. Then,
4500
+ // if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
4501
+ // Finally, iterate over the constituents of the resulting iteration type.
4508
4502
const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType;
4509
4503
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
4510
4504
forEachType(iterationType, t => {
4505
+ // Create a mapper from T to the current iteration type constituent. Then, if the
4506
+ // mapped type is itself an instantiated type, combine the iteration mapper with the
4507
+ // instantiation mapper.
4511
4508
const iterationMapper = createUnaryTypeMapper(typeParameter, t);
4512
4509
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
4513
4510
const propType = instantiateType(templateType, templateMapper);
4511
+ // If the current iteration type constituent is a literal type, create a property.
4512
+ // Otherwise, for type string create a string index signature and for type number
4513
+ // create a numeric index signature.
4514
4514
if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) {
4515
4515
const propName = (<LiteralType>t).text;
4516
4516
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName);
@@ -4525,6 +4525,8 @@ namespace ts {
4525
4525
numberIndexInfo = createIndexInfo(propType, isReadonly);
4526
4526
}
4527
4527
});
4528
+ // If we created both a string and a numeric string index signature, and if the two index
4529
+ // signatures have identical types, discard the redundant numeric index signature.
4528
4530
if (stringIndexInfo && numberIndexInfo && isTypeIdenticalTo(stringIndexInfo.type, numberIndexInfo.type)) {
4529
4531
numberIndexInfo = undefined;
4530
4532
}
@@ -9060,6 +9062,10 @@ namespace ts {
9060
9062
return containsType(target.types, source);
9061
9063
}
9062
9064
9065
+ function forEachType<T>(type: Type, f: (t: Type) => T): T {
9066
+ return type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, f) : f(type);
9067
+ }
9068
+
9063
9069
function filterType(type: Type, f: (t: Type) => boolean): Type {
9064
9070
if (type.flags & TypeFlags.Union) {
9065
9071
const types = (<UnionType>type).types;
0 commit comments