@@ -8436,6 +8436,10 @@ namespace ts {
84368436 return createTypeFromGenericGlobalType(globalArrayType, [elementType]);
84378437 }
84388438
8439+ function createReadonlyArrayType(elementType: Type): ObjectType {
8440+ return createTypeFromGenericGlobalType(globalReadonlyArrayType, [elementType]);
8441+ }
8442+
84398443 function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
84408444 const links = getNodeLinks(node);
84418445 if (!links.resolvedType) {
@@ -10091,11 +10095,16 @@ namespace ts {
1009110095 }
1009210096
1009310097 function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type {
10094- // Check if we have a homomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some
10095- // type variable T. If so, the mapped type is distributive over a union type and when T is instantiated
10096- // to a union type A | B, we produce { [P in keyof A]: X } | { [P in keyof B]: X }. Furthermore, for
10097- // homomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a
10098- // union type A | undefined, we produce { [P in keyof A]: X } | undefined.
10098+ // For a momomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping
10099+ // operation depends on T as follows:
10100+ // * If T is a primitive type no mapping is performed and the result is simply T.
10101+ // * If T is a union type we distribute the mapped type over the union.
10102+ // * If T is an array we map to an array where the element type has been transformed.
10103+ // * If T is a tuple we map to a tuple where the element types have been transformed.
10104+ // * Otherwise we map to an object type where the type of each property has been transformed.
10105+ // For example, when T is instantiated to a union type A | B, we produce { [P in keyof A]: X } |
10106+ // { [P in keyof B]: X }, and when when T is instantiated to a union type A | undefined, we produce
10107+ // { [P in keyof A]: X } | undefined.
1009910108 const constraintType = getConstraintTypeFromMappedType(type);
1010010109 if (constraintType.flags & TypeFlags.Index) {
1010110110 const typeVariable = (<IndexType>constraintType).type;
@@ -10104,7 +10113,11 @@ namespace ts {
1010410113 if (typeVariable !== mappedTypeVariable) {
1010510114 return mapType(mappedTypeVariable, t => {
1010610115 if (isMappableType(t)) {
10107- return instantiateAnonymousType(type, createReplacementMapper(typeVariable, t, mapper));
10116+ const replacementMapper = createReplacementMapper(typeVariable, t, mapper);
10117+ return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
10118+ isReadonlyArrayType(t) ? createReadonlyArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
10119+ isTupleType(t) ? instantiateMappedTupleType(t, type, replacementMapper) :
10120+ instantiateAnonymousType(type, replacementMapper);
1010810121 }
1010910122 return t;
1011010123 });
@@ -10118,6 +10131,26 @@ namespace ts {
1011810131 return type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection);
1011910132 }
1012010133
10134+ function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) {
10135+ const minLength = tupleType.target.minLength;
10136+ const elementTypes = map(tupleType.typeArguments || emptyArray, (_, i) =>
10137+ instantiateMappedTypeTemplate(mappedType, getLiteralType("" + i), i >= minLength, mapper));
10138+ const modifiers = getMappedTypeModifiers(mappedType);
10139+ const newMinLength = modifiers & MappedTypeModifiers.IncludeOptional ? 0 :
10140+ modifiers & MappedTypeModifiers.ExcludeOptional ? getTypeReferenceArity(tupleType) - (tupleType.target.hasRestElement ? 1 : 0) :
10141+ minLength;
10142+ return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, tupleType.target.associatedNames);
10143+ }
10144+
10145+ function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
10146+ const templateMapper = combineTypeMappers(mapper, createTypeMapper([getTypeParameterFromMappedType(type)], [key]));
10147+ const propType = instantiateType(getTemplateTypeFromMappedType(<MappedType>type.target || type), templateMapper);
10148+ const modifiers = getMappedTypeModifiers(type);
10149+ return strictNullChecks && modifiers & MappedTypeModifiers.IncludeOptional && !isTypeAssignableTo(undefinedType, propType) ? getOptionalType(propType) :
10150+ strictNullChecks && modifiers & MappedTypeModifiers.ExcludeOptional && isOptional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) :
10151+ propType;
10152+ }
10153+
1012110154 function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
1012210155 const result = <AnonymousType>createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol);
1012310156 if (type.objectFlags & ObjectFlags.Mapped) {
@@ -12445,6 +12478,10 @@ namespace ts {
1244512478 return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalArrayType;
1244612479 }
1244712480
12481+ function isReadonlyArrayType(type: Type): boolean {
12482+ return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalReadonlyArrayType;
12483+ }
12484+
1244812485 function isArrayLikeType(type: Type): boolean {
1244912486 // A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
1245012487 // or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
@@ -13000,6 +13037,22 @@ namespace ts {
1300013037 return undefined;
1300113038 }
1300213039 }
13040+ // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been
13041+ // applied to the element type(s).
13042+ if (isArrayType(source)) {
13043+ return createArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target));
13044+ }
13045+ if (isReadonlyArrayType(source)) {
13046+ return createReadonlyArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target));
13047+ }
13048+ if (isTupleType(source)) {
13049+ const elementTypes = map(source.typeArguments || emptyArray, t => inferReverseMappedType(t, target));
13050+ const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
13051+ getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength;
13052+ return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.associatedNames);
13053+ }
13054+ // For all other object types we infer a new object type where the reverse mapping has been
13055+ // applied to the type of each property.
1300313056 const reversed = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType;
1300413057 reversed.source = source;
1300513058 reversed.mappedType = target;
0 commit comments