Skip to content

Commit b45a418

Browse files
TypeScript Botahejlsberg
TypeScript Bot
andauthored
🤖 Pick PR #57801 (Distribute mapped types over array/...) into release-5.4 (#57832)
Co-authored-by: Anders Hejlsberg <[email protected]>
1 parent 609560f commit b45a418

File tree

4 files changed

+152
-24
lines changed

4 files changed

+152
-24
lines changed

‎src/compiler/checker.ts

+29-24
Original file line numberDiff line numberDiff line change
@@ -14557,14 +14557,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1455714557
const constraint = getConstraintTypeFromMappedType(type);
1455814558
if (constraint.flags & TypeFlags.Index) {
1455914559
const baseConstraint = getBaseConstraintOfType((constraint as IndexType).type);
14560-
if (baseConstraint && everyType(baseConstraint, isArrayOrTupleType)) {
14560+
if (baseConstraint && everyType(baseConstraint, t => isArrayOrTupleType(t) || isArrayOrTupleOrIntersection(t))) {
1456114561
return instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, type.mapper));
1456214562
}
1456314563
}
1456414564
}
1456514565
return type;
1456614566
}
1456714567

14568+
function isArrayOrTupleOrIntersection(type: Type) {
14569+
return !!(type.flags & TypeFlags.Intersection) && every((type as IntersectionType).types, isArrayOrTupleType);
14570+
}
14571+
1456814572
function isMappedTypeGenericIndexedAccess(type: Type) {
1456914573
let objectType;
1457014574
return !!(type.flags & TypeFlags.IndexedAccess && getObjectFlags(objectType = (type as IndexedAccessType).objectType) & ObjectFlags.Mapped &&
@@ -19759,6 +19763,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1975919763
// * If T is a union type we distribute the mapped type over the union.
1976019764
// * If T is an array we map to an array where the element type has been transformed.
1976119765
// * If T is a tuple we map to a tuple where the element types have been transformed.
19766+
// * If T is an intersection of array or tuple types we map to an intersection of transformed array or tuple types.
1976219767
// * Otherwise we map to an object type where the type of each property has been transformed.
1976319768
// For example, when T is instantiated to a union type A | B, we produce { [P in keyof A]: X } |
1976419769
// { [P in keyof B]: X }, and when when T is instantiated to a union type A | undefined, we produce
@@ -19767,33 +19772,33 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1976719772
if (typeVariable) {
1976819773
const mappedTypeVariable = instantiateType(typeVariable, mapper);
1976919774
if (typeVariable !== mappedTypeVariable) {
19770-
return mapTypeWithAlias(
19771-
getReducedType(mappedTypeVariable),
19772-
t => {
19773-
if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType && !isErrorType(t)) {
19774-
if (!type.declaration.nameType) {
19775-
let constraint;
19776-
if (
19777-
isArrayType(t) || t.flags & TypeFlags.Any && findResolutionCycleStartIndex(typeVariable, TypeSystemPropertyName.ImmediateBaseConstraint) < 0 &&
19778-
(constraint = getConstraintOfTypeParameter(typeVariable)) && everyType(constraint, isArrayOrTupleType)
19779-
) {
19780-
return instantiateMappedArrayType(t, type, prependTypeMapping(typeVariable, t, mapper));
19781-
}
19782-
if (isTupleType(t)) {
19783-
return instantiateMappedTupleType(t, type, typeVariable, mapper);
19784-
}
19785-
}
19786-
return instantiateAnonymousType(type, prependTypeMapping(typeVariable, t, mapper));
19787-
}
19788-
return t;
19789-
},
19790-
aliasSymbol,
19791-
aliasTypeArguments,
19792-
);
19775+
return mapTypeWithAlias(getReducedType(mappedTypeVariable), instantiateConstituent, aliasSymbol, aliasTypeArguments);
1979319776
}
1979419777
}
1979519778
// If the constraint type of the instantiation is the wildcard type, return the wildcard type.
1979619779
return instantiateType(getConstraintTypeFromMappedType(type), mapper) === wildcardType ? wildcardType : instantiateAnonymousType(type, mapper, aliasSymbol, aliasTypeArguments);
19780+
19781+
function instantiateConstituent(t: Type): Type {
19782+
if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType && !isErrorType(t)) {
19783+
if (!type.declaration.nameType) {
19784+
let constraint;
19785+
if (
19786+
isArrayType(t) || t.flags & TypeFlags.Any && findResolutionCycleStartIndex(typeVariable!, TypeSystemPropertyName.ImmediateBaseConstraint) < 0 &&
19787+
(constraint = getConstraintOfTypeParameter(typeVariable!)) && everyType(constraint, isArrayOrTupleType)
19788+
) {
19789+
return instantiateMappedArrayType(t, type, prependTypeMapping(typeVariable!, t, mapper));
19790+
}
19791+
if (isTupleType(t)) {
19792+
return instantiateMappedTupleType(t, type, typeVariable!, mapper);
19793+
}
19794+
if (isArrayOrTupleOrIntersection(t)) {
19795+
return getIntersectionType(map((t as IntersectionType).types, instantiateConstituent));
19796+
}
19797+
}
19798+
return instantiateAnonymousType(type, prependTypeMapping(typeVariable!, t, mapper));
19799+
}
19800+
return t;
19801+
}
1979719802
}
1979819803

1979919804
function getModifiedReadonlyState(state: boolean, modifiers: MappedTypeModifiers) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//// [tests/cases/compiler/mappedArrayTupleIntersections.ts] ////
2+
3+
=== mappedArrayTupleIntersections.ts ===
4+
type Box<T> = { value: T };
5+
>Box : Symbol(Box, Decl(mappedArrayTupleIntersections.ts, 0, 0))
6+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 0, 9))
7+
>value : Symbol(value, Decl(mappedArrayTupleIntersections.ts, 0, 15))
8+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 0, 9))
9+
10+
type Boxify<T> = { [K in keyof T]: Box<T[K]> };
11+
>Boxify : Symbol(Boxify, Decl(mappedArrayTupleIntersections.ts, 0, 27))
12+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 1, 12))
13+
>K : Symbol(K, Decl(mappedArrayTupleIntersections.ts, 1, 20))
14+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 1, 12))
15+
>Box : Symbol(Box, Decl(mappedArrayTupleIntersections.ts, 0, 0))
16+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 1, 12))
17+
>K : Symbol(K, Decl(mappedArrayTupleIntersections.ts, 1, 20))
18+
19+
type T1 = Boxify<string[]>;
20+
>T1 : Symbol(T1, Decl(mappedArrayTupleIntersections.ts, 1, 47))
21+
>Boxify : Symbol(Boxify, Decl(mappedArrayTupleIntersections.ts, 0, 27))
22+
23+
type T2 = Boxify<[string, string]>;
24+
>T2 : Symbol(T2, Decl(mappedArrayTupleIntersections.ts, 3, 27))
25+
>Boxify : Symbol(Boxify, Decl(mappedArrayTupleIntersections.ts, 0, 27))
26+
27+
type T3 = Boxify<string[] & unknown[]>;
28+
>T3 : Symbol(T3, Decl(mappedArrayTupleIntersections.ts, 4, 35))
29+
>Boxify : Symbol(Boxify, Decl(mappedArrayTupleIntersections.ts, 0, 27))
30+
31+
type T4 = Boxify<string[] & [string, string]>;
32+
>T4 : Symbol(T4, Decl(mappedArrayTupleIntersections.ts, 5, 39))
33+
>Boxify : Symbol(Boxify, Decl(mappedArrayTupleIntersections.ts, 0, 27))
34+
35+
type T5 = Boxify<string[] & { x: string }>;
36+
>T5 : Symbol(T5, Decl(mappedArrayTupleIntersections.ts, 6, 46))
37+
>Boxify : Symbol(Boxify, Decl(mappedArrayTupleIntersections.ts, 0, 27))
38+
>x : Symbol(x, Decl(mappedArrayTupleIntersections.ts, 7, 29))
39+
40+
// https://github.com/microsoft/TypeScript/issues/57744
41+
42+
type MustBeArray<T extends any[]> = T;
43+
>MustBeArray : Symbol(MustBeArray, Decl(mappedArrayTupleIntersections.ts, 7, 43))
44+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 11, 17))
45+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 11, 17))
46+
47+
type Hmm<T extends any[]> = T extends number[] ?
48+
>Hmm : Symbol(Hmm, Decl(mappedArrayTupleIntersections.ts, 11, 38))
49+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 13, 9))
50+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 13, 9))
51+
52+
MustBeArray<{ [I in keyof T]: 1 }> :
53+
>MustBeArray : Symbol(MustBeArray, Decl(mappedArrayTupleIntersections.ts, 7, 43))
54+
>I : Symbol(I, Decl(mappedArrayTupleIntersections.ts, 14, 19))
55+
>T : Symbol(T, Decl(mappedArrayTupleIntersections.ts, 13, 9))
56+
57+
never;
58+
59+
type X = Hmm<[3, 4, 5]>;
60+
>X : Symbol(X, Decl(mappedArrayTupleIntersections.ts, 15, 10))
61+
>Hmm : Symbol(Hmm, Decl(mappedArrayTupleIntersections.ts, 11, 38))
62+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [tests/cases/compiler/mappedArrayTupleIntersections.ts] ////
2+
3+
=== mappedArrayTupleIntersections.ts ===
4+
type Box<T> = { value: T };
5+
>Box : Box<T>
6+
>value : T
7+
8+
type Boxify<T> = { [K in keyof T]: Box<T[K]> };
9+
>Boxify : Boxify<T>
10+
11+
type T1 = Boxify<string[]>;
12+
>T1 : Box<string>[]
13+
14+
type T2 = Boxify<[string, string]>;
15+
>T2 : [Box<string>, Box<string>]
16+
17+
type T3 = Boxify<string[] & unknown[]>;
18+
>T3 : Box<string>[] & Box<unknown>[]
19+
20+
type T4 = Boxify<string[] & [string, string]>;
21+
>T4 : Box<string>[] & [Box<string>, Box<string>]
22+
23+
type T5 = Boxify<string[] & { x: string }>;
24+
>T5 : Boxify<string[] & { x: string; }>
25+
>x : string
26+
27+
// https://github.com/microsoft/TypeScript/issues/57744
28+
29+
type MustBeArray<T extends any[]> = T;
30+
>MustBeArray : T
31+
32+
type Hmm<T extends any[]> = T extends number[] ?
33+
>Hmm : Hmm<T>
34+
35+
MustBeArray<{ [I in keyof T]: 1 }> :
36+
never;
37+
38+
type X = Hmm<[3, 4, 5]>;
39+
>X : [1, 1, 1]
40+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
type Box<T> = { value: T };
5+
type Boxify<T> = { [K in keyof T]: Box<T[K]> };
6+
7+
type T1 = Boxify<string[]>;
8+
type T2 = Boxify<[string, string]>;
9+
type T3 = Boxify<string[] & unknown[]>;
10+
type T4 = Boxify<string[] & [string, string]>;
11+
type T5 = Boxify<string[] & { x: string }>;
12+
13+
// https://github.com/microsoft/TypeScript/issues/57744
14+
15+
type MustBeArray<T extends any[]> = T;
16+
17+
type Hmm<T extends any[]> = T extends number[] ?
18+
MustBeArray<{ [I in keyof T]: 1 }> :
19+
never;
20+
21+
type X = Hmm<[3, 4, 5]>;

0 commit comments

Comments
 (0)