Skip to content

Commit b45a418

Browse files
TypeScript Botahejlsberg
andauthored
🤖 Pick PR #57801 (Distribute mapped types over array/...) into release-5.4 (#57832)
Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
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

Lines changed: 29 additions & 24 deletions
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) {
Lines changed: 62 additions & 0 deletions
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+
Lines changed: 40 additions & 0 deletions
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+
Lines changed: 21 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy