Skip to content

Commit 9f33bf1

Browse files
TypeScript Botahejlsberg
TypeScript Bot
andauthored
🤖 Pick PR #58098 (Fix constraints of nested homomorph...) into release-5.4 (#58118)
Co-authored-by: Anders Hejlsberg <[email protected]>
1 parent 71b2f84 commit 9f33bf1

File tree

4 files changed

+103
-7
lines changed

4 files changed

+103
-7
lines changed

Diff for: ‎src/compiler/checker.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -14552,16 +14552,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1455214552
return type.resolvedApparentType || (type.resolvedApparentType = getResolvedApparentTypeOfMappedType(type));
1455314553
}
1455414554

14555-
function getResolvedApparentTypeOfMappedType(type: MappedType) {
14555+
function getResolvedApparentTypeOfMappedType(type: MappedType): Type {
1455614556
const target = (type.target ?? type) as MappedType;
1455714557
const typeVariable = getHomomorphicTypeVariable(target);
1455814558
if (typeVariable && !target.declaration.nameType) {
14559-
const constraint = getConstraintTypeFromMappedType(type);
14560-
if (constraint.flags & TypeFlags.Index) {
14561-
const baseConstraint = getBaseConstraintOfType((constraint as IndexType).type);
14562-
if (baseConstraint && everyType(baseConstraint, t => isArrayOrTupleType(t) || isArrayOrTupleOrIntersection(t))) {
14563-
return instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, type.mapper));
14564-
}
14559+
// We have a homomorphic mapped type or an instantiation of a homomorphic mapped type, i.e. a type
14560+
// of the form { [P in keyof T]: X }. Obtain the modifiers type (the T of the keyof T), and if it is
14561+
// another generic mapped type, recursively obtain its apparent type. Otherwise, obtain its base
14562+
// constraint. Then, if every constituent of the base constraint is an array or tuple type, apply
14563+
// this mapped type to the base constraint. It is safe to recurse when the modifiers type is a
14564+
// mapped type because we protect again circular constraints in getTypeFromMappedTypeNode.
14565+
const modifiersType = getModifiersTypeFromMappedType(type);
14566+
const baseConstraint = isGenericMappedType(modifiersType) ? getApparentTypeOfMappedType(modifiersType) : getBaseConstraintOfType(modifiersType);
14567+
if (baseConstraint && everyType(baseConstraint, t => isArrayOrTupleType(t) || isArrayOrTupleOrIntersection(t))) {
14568+
return instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, type.mapper));
1456514569
}
1456614570
}
1456714571
return type;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//// [tests/cases/compiler/homomorphicMappedTypeNesting.ts] ////
2+
3+
=== homomorphicMappedTypeNesting.ts ===
4+
// Repro from #58060
5+
6+
type Box<T extends string> = { v: T };
7+
>Box : Symbol(Box, Decl(homomorphicMappedTypeNesting.ts, 0, 0))
8+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 2, 9))
9+
>v : Symbol(v, Decl(homomorphicMappedTypeNesting.ts, 2, 30))
10+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 2, 9))
11+
12+
type Test<T extends string[]> = T
13+
>Test : Symbol(Test, Decl(homomorphicMappedTypeNesting.ts, 2, 38))
14+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 4, 10))
15+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 4, 10))
16+
17+
type UnboxArray<T> = {
18+
>UnboxArray : Symbol(UnboxArray, Decl(homomorphicMappedTypeNesting.ts, 4, 33))
19+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 6, 16))
20+
21+
[K in keyof T]: T[K] extends Box<infer R> ? R : never;
22+
>K : Symbol(K, Decl(homomorphicMappedTypeNesting.ts, 7, 5))
23+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 6, 16))
24+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 6, 16))
25+
>K : Symbol(K, Decl(homomorphicMappedTypeNesting.ts, 7, 5))
26+
>Box : Symbol(Box, Decl(homomorphicMappedTypeNesting.ts, 0, 0))
27+
>R : Symbol(R, Decl(homomorphicMappedTypeNesting.ts, 7, 42))
28+
>R : Symbol(R, Decl(homomorphicMappedTypeNesting.ts, 7, 42))
29+
30+
};
31+
32+
type Identity<T> = { [K in keyof T]: T[K] };
33+
>Identity : Symbol(Identity, Decl(homomorphicMappedTypeNesting.ts, 8, 2))
34+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 10, 14))
35+
>K : Symbol(K, Decl(homomorphicMappedTypeNesting.ts, 10, 22))
36+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 10, 14))
37+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 10, 14))
38+
>K : Symbol(K, Decl(homomorphicMappedTypeNesting.ts, 10, 22))
39+
40+
declare function fnBad<T extends Array<Box<string>>>(...args: T): Test<Identity<UnboxArray<T>>>;
41+
>fnBad : Symbol(fnBad, Decl(homomorphicMappedTypeNesting.ts, 10, 44))
42+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 12, 23))
43+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
44+
>Box : Symbol(Box, Decl(homomorphicMappedTypeNesting.ts, 0, 0))
45+
>args : Symbol(args, Decl(homomorphicMappedTypeNesting.ts, 12, 53))
46+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 12, 23))
47+
>Test : Symbol(Test, Decl(homomorphicMappedTypeNesting.ts, 2, 38))
48+
>Identity : Symbol(Identity, Decl(homomorphicMappedTypeNesting.ts, 8, 2))
49+
>UnboxArray : Symbol(UnboxArray, Decl(homomorphicMappedTypeNesting.ts, 4, 33))
50+
>T : Symbol(T, Decl(homomorphicMappedTypeNesting.ts, 12, 23))
51+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [tests/cases/compiler/homomorphicMappedTypeNesting.ts] ////
2+
3+
=== homomorphicMappedTypeNesting.ts ===
4+
// Repro from #58060
5+
6+
type Box<T extends string> = { v: T };
7+
>Box : Box<T>
8+
>v : T
9+
10+
type Test<T extends string[]> = T
11+
>Test : T
12+
13+
type UnboxArray<T> = {
14+
>UnboxArray : UnboxArray<T>
15+
16+
[K in keyof T]: T[K] extends Box<infer R> ? R : never;
17+
};
18+
19+
type Identity<T> = { [K in keyof T]: T[K] };
20+
>Identity : Identity<T>
21+
22+
declare function fnBad<T extends Array<Box<string>>>(...args: T): Test<Identity<UnboxArray<T>>>;
23+
>fnBad : <T extends Box<string>[]>(...args: T) => Test<Identity<UnboxArray<T>>>
24+
>args : T
25+
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// Repro from #58060
5+
6+
type Box<T extends string> = { v: T };
7+
8+
type Test<T extends string[]> = T
9+
10+
type UnboxArray<T> = {
11+
[K in keyof T]: T[K] extends Box<infer R> ? R : never;
12+
};
13+
14+
type Identity<T> = { [K in keyof T]: T[K] };
15+
16+
declare function fnBad<T extends Array<Box<string>>>(...args: T): Test<Identity<UnboxArray<T>>>;

0 commit comments

Comments
 (0)