@@ -8432,6 +8432,10 @@ namespace ts {
8432
8432
return createTypeFromGenericGlobalType(globalArrayType, [elementType]);
8433
8433
}
8434
8434
8435
+ function createReadonlyArrayType(elementType: Type): ObjectType {
8436
+ return createTypeFromGenericGlobalType(globalReadonlyArrayType, [elementType]);
8437
+ }
8438
+
8435
8439
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
8436
8440
const links = getNodeLinks(node);
8437
8441
if (!links.resolvedType) {
@@ -10087,11 +10091,16 @@ namespace ts {
10087
10091
}
10088
10092
10089
10093
function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type {
10090
- // Check if we have a homomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some
10091
- // type variable T. If so, the mapped type is distributive over a union type and when T is instantiated
10092
- // to a union type A | B, we produce { [P in keyof A]: X } | { [P in keyof B]: X }. Furthermore, for
10093
- // homomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a
10094
- // union type A | undefined, we produce { [P in keyof A]: X } | undefined.
10094
+ // For a momomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping
10095
+ // operation depends on T as follows:
10096
+ // * If T is a primitive type no mapping is performed and the result is simply T.
10097
+ // * If T is a union type we distribute the mapped type over the union.
10098
+ // * If T is an array we map to an array where the element type has been transformed.
10099
+ // * If T is a tuple we map to a tuple where the element types have been transformed.
10100
+ // * Otherwise we map to an object type where the type of each property has been transformed.
10101
+ // For example, when T is instantiated to a union type A | B, we produce { [P in keyof A]: X } |
10102
+ // { [P in keyof B]: X }, and when when T is instantiated to a union type A | undefined, we produce
10103
+ // { [P in keyof A]: X } | undefined.
10095
10104
const constraintType = getConstraintTypeFromMappedType(type);
10096
10105
if (constraintType.flags & TypeFlags.Index) {
10097
10106
const typeVariable = (<IndexType>constraintType).type;
@@ -10100,7 +10109,11 @@ namespace ts {
10100
10109
if (typeVariable !== mappedTypeVariable) {
10101
10110
return mapType(mappedTypeVariable, t => {
10102
10111
if (isMappableType(t)) {
10103
- return instantiateAnonymousType(type, createReplacementMapper(typeVariable, t, mapper));
10112
+ const replacementMapper = createReplacementMapper(typeVariable, t, mapper);
10113
+ return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
10114
+ isReadonlyArrayType(t) ? createReadonlyArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper)) :
10115
+ isTupleType(t) ? instantiateMappedTupleType(t, type, replacementMapper) :
10116
+ instantiateAnonymousType(type, replacementMapper);
10104
10117
}
10105
10118
return t;
10106
10119
});
@@ -10114,6 +10127,26 @@ namespace ts {
10114
10127
return type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection);
10115
10128
}
10116
10129
10130
+ function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) {
10131
+ const minLength = tupleType.target.minLength;
10132
+ const elementTypes = map(tupleType.typeArguments || emptyArray, (_, i) =>
10133
+ instantiateMappedTypeTemplate(mappedType, getLiteralType("" + i), i >= minLength, mapper));
10134
+ const modifiers = getMappedTypeModifiers(mappedType);
10135
+ const newMinLength = modifiers & MappedTypeModifiers.IncludeOptional ? 0 :
10136
+ modifiers & MappedTypeModifiers.ExcludeOptional ? getTypeReferenceArity(tupleType) - (tupleType.target.hasRestElement ? 1 : 0) :
10137
+ minLength;
10138
+ return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, tupleType.target.associatedNames);
10139
+ }
10140
+
10141
+ function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
10142
+ const templateMapper = combineTypeMappers(mapper, createTypeMapper([getTypeParameterFromMappedType(type)], [key]));
10143
+ const propType = instantiateType(getTemplateTypeFromMappedType(<MappedType>type.target || type), templateMapper);
10144
+ const modifiers = getMappedTypeModifiers(type);
10145
+ return strictNullChecks && modifiers & MappedTypeModifiers.IncludeOptional && !isTypeAssignableTo(undefinedType, propType) ? getOptionalType(propType) :
10146
+ strictNullChecks && modifiers & MappedTypeModifiers.ExcludeOptional && isOptional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) :
10147
+ propType;
10148
+ }
10149
+
10117
10150
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
10118
10151
const result = <AnonymousType>createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol);
10119
10152
if (type.objectFlags & ObjectFlags.Mapped) {
@@ -12441,6 +12474,10 @@ namespace ts {
12441
12474
return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalArrayType;
12442
12475
}
12443
12476
12477
+ function isReadonlyArrayType(type: Type): boolean {
12478
+ return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalReadonlyArrayType;
12479
+ }
12480
+
12444
12481
function isArrayLikeType(type: Type): boolean {
12445
12482
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
12446
12483
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
@@ -12996,6 +13033,22 @@ namespace ts {
12996
13033
return undefined;
12997
13034
}
12998
13035
}
13036
+ // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been
13037
+ // applied to the element type(s).
13038
+ if (isArrayType(source)) {
13039
+ return createArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target));
13040
+ }
13041
+ if (isReadonlyArrayType(source)) {
13042
+ return createReadonlyArrayType(inferReverseMappedType((<TypeReference>source).typeArguments![0], target));
13043
+ }
13044
+ if (isTupleType(source)) {
13045
+ const elementTypes = map(source.typeArguments || emptyArray, t => inferReverseMappedType(t, target));
13046
+ const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
13047
+ getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength;
13048
+ return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.associatedNames);
13049
+ }
13050
+ // For all other object types we infer a new object type where the reverse mapping has been
13051
+ // applied to the type of each property.
12999
13052
const reversed = createObjectType(ObjectFlags.ReverseMapped | ObjectFlags.Anonymous, /*symbol*/ undefined) as ReverseMappedType;
13000
13053
reversed.source = source;
13001
13054
reversed.mappedType = target;
0 commit comments