Skip to content

Commit 424b964

Browse files
authored
Fixed apparent type of homomorphic mapped type with non-homomorphic instantiation (#56727)
1 parent 1e00399 commit 424b964

4 files changed

+252
-5
lines changed

src/compiler/checker.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,6 +2049,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
20492049
var noConstraintType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
20502050
var circularConstraintType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
20512051
var resolvingDefaultType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
2052+
var resolvingApparentMappedType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
20522053

20532054
var markerSuperType = createTypeParameter();
20542055
var markerSubType = createTypeParameter();
@@ -14459,15 +14460,32 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1445914460
}
1446014461

1446114462
function getApparentTypeOfMappedType(type: MappedType) {
14462-
return type.resolvedApparentType || (type.resolvedApparentType = getResolvedApparentTypeOfMappedType(type));
14463+
if (type.resolvedApparentType) {
14464+
if (type.resolvedApparentType === resolvingApparentMappedType) {
14465+
return type.resolvedApparentType = type;
14466+
}
14467+
return type.resolvedApparentType;
14468+
}
14469+
type.resolvedApparentType = resolvingApparentMappedType;
14470+
return type.resolvedApparentType = getResolvedApparentTypeOfMappedType(type);
1446314471
}
1446414472

1446514473
function getResolvedApparentTypeOfMappedType(type: MappedType) {
14466-
const typeVariable = getHomomorphicTypeVariable(type);
14467-
if (typeVariable && !type.declaration.nameType) {
14468-
const constraint = getConstraintOfTypeParameter(typeVariable);
14474+
const mappedType = type.target as MappedType || type;
14475+
const typeVariable = getHomomorphicTypeVariable(mappedType);
14476+
if (typeVariable && !mappedType.declaration.nameType) {
14477+
let constraint: Type | undefined;
14478+
if (!type.target) {
14479+
constraint = getConstraintOfTypeParameter(typeVariable);
14480+
}
14481+
else {
14482+
const modifiersConstraint = getConstraintOfType(getModifiersTypeFromMappedType(type));
14483+
if (modifiersConstraint) {
14484+
constraint = getApparentType(modifiersConstraint);
14485+
}
14486+
}
1446914487
if (constraint && everyType(constraint, isArrayOrTupleType)) {
14470-
return instantiateType(type, prependTypeMapping(typeVariable, constraint, type.mapper));
14488+
return instantiateType(mappedType, prependTypeMapping(typeVariable, constraint, mappedType.mapper));
1447114489
}
1447214490
}
1447314491
return type;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//// [tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts] ////
2+
3+
=== homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts ===
4+
type HandleOptions<O> = {
5+
>HandleOptions : Symbol(HandleOptions, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 0))
6+
>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19))
7+
8+
[I in keyof O]: {
9+
>I : Symbol(I, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 3))
10+
>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19))
11+
12+
value: O[I];
13+
>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 19))
14+
>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19))
15+
>I : Symbol(I, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 3))
16+
17+
};
18+
};
19+
20+
declare function func1<
21+
>func1 : Symbol(func1, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 4, 2))
22+
23+
T extends Record<PropertyKey, readonly any[]>,
24+
>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23))
25+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
26+
>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --))
27+
28+
>(fields: {
29+
>fields : Symbol(fields, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 8, 2))
30+
31+
[K in keyof T]: {
32+
>K : Symbol(K, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 3))
33+
>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23))
34+
35+
label: string;
36+
>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 19))
37+
38+
options: [...HandleOptions<T[K]>];
39+
>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 10, 18))
40+
>HandleOptions : Symbol(HandleOptions, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 0))
41+
>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23))
42+
>K : Symbol(K, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 3))
43+
44+
};
45+
}): T;
46+
>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23))
47+
48+
const result = func1({
49+
>result : Symbol(result, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 15, 5))
50+
>func1 : Symbol(func1, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 4, 2))
51+
52+
prop: {
53+
>prop : Symbol(prop, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 15, 22))
54+
55+
label: "first",
56+
>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 16, 9))
57+
58+
options: [
59+
>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 17, 19))
60+
{
61+
value: 123,
62+
>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 19, 7))
63+
64+
},
65+
{
66+
value: "foo",
67+
>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 22, 7))
68+
69+
},
70+
],
71+
},
72+
other: {
73+
>other : Symbol(other, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 26, 4))
74+
75+
label: "second",
76+
>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 27, 10))
77+
78+
options: [
79+
>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 28, 20))
80+
{
81+
value: "bar",
82+
>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 30, 7))
83+
84+
},
85+
{
86+
value: true,
87+
>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 33, 7))
88+
89+
},
90+
],
91+
},
92+
});
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//// [tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts] ////
2+
3+
=== homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts ===
4+
type HandleOptions<O> = {
5+
>HandleOptions : HandleOptions<O>
6+
7+
[I in keyof O]: {
8+
value: O[I];
9+
>value : O[I]
10+
11+
};
12+
};
13+
14+
declare function func1<
15+
>func1 : <T extends Record<PropertyKey, readonly any[]>>(fields: { [K in keyof T]: { label: string; options: [...HandleOptions<T[K]>]; }; }) => T
16+
17+
T extends Record<PropertyKey, readonly any[]>,
18+
>(fields: {
19+
>fields : { [K in keyof T]: { label: string; options: [...HandleOptions<T[K]>]; }; }
20+
21+
[K in keyof T]: {
22+
label: string;
23+
>label : string
24+
25+
options: [...HandleOptions<T[K]>];
26+
>options : [...HandleOptions<T[K]>]
27+
28+
};
29+
}): T;
30+
31+
const result = func1({
32+
>result : { prop: [number, string]; other: [string, boolean]; }
33+
>func1({ prop: { label: "first", options: [ { value: 123, }, { value: "foo", }, ], }, other: { label: "second", options: [ { value: "bar", }, { value: true, }, ], },}) : { prop: [number, string]; other: [string, boolean]; }
34+
>func1 : <T extends Record<PropertyKey, readonly any[]>>(fields: { [K in keyof T]: { label: string; options: [...HandleOptions<T[K]>]; }; }) => T
35+
>{ prop: { label: "first", options: [ { value: 123, }, { value: "foo", }, ], }, other: { label: "second", options: [ { value: "bar", }, { value: true, }, ], },} : { prop: { label: string; options: [{ value: number; }, { value: string; }]; }; other: { label: string; options: [{ value: string; }, { value: true; }]; }; }
36+
37+
prop: {
38+
>prop : { label: string; options: [{ value: number; }, { value: string; }]; }
39+
>{ label: "first", options: [ { value: 123, }, { value: "foo", }, ], } : { label: string; options: [{ value: number; }, { value: string; }]; }
40+
41+
label: "first",
42+
>label : string
43+
>"first" : "first"
44+
45+
options: [
46+
>options : [{ value: number; }, { value: string; }]
47+
>[ { value: 123, }, { value: "foo", }, ] : [{ value: number; }, { value: string; }]
48+
{
49+
>{ value: 123, } : { value: number; }
50+
51+
value: 123,
52+
>value : number
53+
>123 : 123
54+
55+
},
56+
{
57+
>{ value: "foo", } : { value: string; }
58+
59+
value: "foo",
60+
>value : string
61+
>"foo" : "foo"
62+
63+
},
64+
],
65+
},
66+
other: {
67+
>other : { label: string; options: [{ value: string; }, { value: true; }]; }
68+
>{ label: "second", options: [ { value: "bar", }, { value: true, }, ], } : { label: string; options: [{ value: string; }, { value: true; }]; }
69+
70+
label: "second",
71+
>label : string
72+
>"second" : "second"
73+
74+
options: [
75+
>options : [{ value: string; }, { value: true; }]
76+
>[ { value: "bar", }, { value: true, }, ] : [{ value: string; }, { value: true; }]
77+
{
78+
>{ value: "bar", } : { value: string; }
79+
80+
value: "bar",
81+
>value : string
82+
>"bar" : "bar"
83+
84+
},
85+
{
86+
>{ value: true, } : { value: true; }
87+
88+
value: true,
89+
>value : true
90+
>true : true
91+
92+
},
93+
],
94+
},
95+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
type HandleOptions<O> = {
5+
[I in keyof O]: {
6+
value: O[I];
7+
};
8+
};
9+
10+
declare function func1<
11+
T extends Record<PropertyKey, readonly any[]>,
12+
>(fields: {
13+
[K in keyof T]: {
14+
label: string;
15+
options: [...HandleOptions<T[K]>];
16+
};
17+
}): T;
18+
19+
const result = func1({
20+
prop: {
21+
label: "first",
22+
options: [
23+
{
24+
value: 123,
25+
},
26+
{
27+
value: "foo",
28+
},
29+
],
30+
},
31+
other: {
32+
label: "second",
33+
options: [
34+
{
35+
value: "bar",
36+
},
37+
{
38+
value: true,
39+
},
40+
],
41+
},
42+
});

0 commit comments

Comments
 (0)