Skip to content

Commit db1c873

Browse files
committed
Fixed apparent type of homomorphic mapped type with non-homomorphic instantiation
1 parent e3d234c commit db1c873

4 files changed

+249
-4
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,6 +2126,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
21262126
var amalgamatedDuplicates: Map<string, DuplicateInfoForFiles> | undefined;
21272127
var reverseMappedCache = new Map<string, Type | undefined>();
21282128
var homomorphicMappedTypeInferenceStack: string[] = [];
2129+
var homomorphicMappedTypeApparentTypeStack: Type[] = [];
21292130
var ambientModulesCache: Symbol[] | undefined;
21302131
/**
21312132
* List of every ambient module with a "*" wildcard.
@@ -14441,11 +14442,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1444114442
}
1444214443

1444314444
function getResolvedApparentTypeOfMappedType(type: MappedType) {
14444-
const typeVariable = getHomomorphicTypeVariable(type);
14445-
if (typeVariable && !type.declaration.nameType) {
14446-
const constraint = getConstraintOfTypeParameter(typeVariable);
14445+
if (contains(homomorphicMappedTypeApparentTypeStack, type)) {
14446+
return type;
14447+
}
14448+
const mappedType = type.target as MappedType || type;
14449+
const typeVariable = getHomomorphicTypeVariable(mappedType);
14450+
if (typeVariable && !mappedType.declaration.nameType) {
14451+
let constraint: Type | undefined;
14452+
if (!type.target) {
14453+
constraint = getConstraintOfTypeParameter(typeVariable);
14454+
}
14455+
else {
14456+
const modifiersConstraint = getConstraintOfType(getModifiersTypeFromMappedType(type));
14457+
if (modifiersConstraint) {
14458+
homomorphicMappedTypeApparentTypeStack.push(type);
14459+
constraint = getApparentType(modifiersConstraint);
14460+
homomorphicMappedTypeApparentTypeStack.pop();
14461+
}
14462+
}
1444714463
if (constraint && everyType(constraint, isArrayOrTupleType)) {
14448-
return instantiateType(type, prependTypeMapping(typeVariable, constraint, type.mapper));
14464+
return instantiateType(mappedType, prependTypeMapping(typeVariable, constraint, mappedType.mapper));
1444914465
}
1445014466
}
1445114467
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)