@@ -4484,33 +4484,33 @@ namespace ts {
44844484 }
44854485 }
44864486
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 } */
44994488 function resolveMappedTypeMembers(type: MappedType) {
45004489 const members: SymbolTable = createMap<Symbol>();
45014490 let stringIndexInfo: IndexInfo;
45024491 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.
45034494 const typeParameter = getTypeParameterFromMappedType(type);
45044495 const constraintType = getConstraintTypeFromMappedType(type);
45054496 const templateType = getTemplateTypeFromMappedType(type);
45064497 const isReadonly = !!type.declaration.readonlyToken;
45074498 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.
45084502 const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType;
45094503 const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
45104504 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.
45114508 const iterationMapper = createUnaryTypeMapper(typeParameter, t);
45124509 const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
45134510 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.
45144514 if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) {
45154515 const propName = (<LiteralType>t).text;
45164516 const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName);
@@ -4525,6 +4525,8 @@ namespace ts {
45254525 numberIndexInfo = createIndexInfo(propType, isReadonly);
45264526 }
45274527 });
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.
45284530 if (stringIndexInfo && numberIndexInfo && isTypeIdenticalTo(stringIndexInfo.type, numberIndexInfo.type)) {
45294531 numberIndexInfo = undefined;
45304532 }
@@ -9060,6 +9062,10 @@ namespace ts {
90609062 return containsType(target.types, source);
90619063 }
90629064
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+
90639069 function filterType(type: Type, f: (t: Type) => boolean): Type {
90649070 if (type.flags & TypeFlags.Union) {
90659071 const types = (<UnionType>type).types;
0 commit comments