@@ -8436,6 +8436,10 @@ namespace ts {
8436
8436
return createTypeFromGenericGlobalType(globalArrayType, [elementType]);
8437
8437
}
8438
8438
8439
+ function createReadonlyArrayType(elementType: Type): ObjectType {
8440
+ return createTypeFromGenericGlobalType(globalReadonlyArrayType, [elementType]);
8441
+ }
8442
+
8439
8443
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
8440
8444
const links = getNodeLinks(node);
8441
8445
if (!links.resolvedType) {
@@ -10091,11 +10095,16 @@ namespace ts {
10091
10095
}
10092
10096
10093
10097
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.
10099
10108
const constraintType = getConstraintTypeFromMappedType(type);
10100
10109
if (constraintType.flags & TypeFlags.Index) {
10101
10110
const typeVariable = (<IndexType>constraintType).type;
@@ -10104,7 +10113,11 @@ namespace ts {
10104
10113
if (typeVariable !== mappedTypeVariable) {
10105
10114
return mapType(mappedTypeVariable, t => {
10106
10115
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);
10108
10121
}
10109
10122
return t;
10110
10123
});
@@ -10118,6 +10131,26 @@ namespace ts {
10118
10131
return type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection);
10119
10132
}
10120
10133
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
+
10121
10154
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
10122
10155
const result = <AnonymousType>createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol);
10123
10156
if (type.objectFlags & ObjectFlags.Mapped) {
@@ -12445,6 +12478,10 @@ namespace ts {
12445
12478
return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalArrayType;
12446
12479
}
12447
12480
12481
+ function isReadonlyArrayType(type: Type): boolean {
12482
+ return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalReadonlyArrayType;
12483
+ }
12484
+
12448
12485
function isArrayLikeType(type: Type): boolean {
12449
12486
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
12450
12487
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
@@ -13000,6 +13037,22 @@ namespace ts {
13000
13037
return undefined;
13001
13038
}
13002
13039
}
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.
13003
13056
const reversed = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType;
13004
13057
reversed.source = source;
13005
13058
reversed.mappedType = target;
0 commit comments