Skip to content

Commit c474123

Browse files
authored
Fixed const reverse mapped types themselves to be treated as const (#55794)
1 parent 2d4cacd commit c474123

File tree

5 files changed

+315
-2
lines changed

5 files changed

+315
-2
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14055,13 +14055,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1405514055
return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined;
1405614056
}
1405714057

14058+
function isConstMappedType(type: MappedType, depth: number): boolean {
14059+
const typeVariable = getHomomorphicTypeVariable(type);
14060+
return !!typeVariable && isConstTypeVariable(typeVariable, depth);
14061+
}
14062+
1405814063
function isConstTypeVariable(type: Type | undefined, depth = 0): boolean {
1405914064
return depth < 5 && !!(type && (
1406014065
type.flags & TypeFlags.TypeParameter && some((type as TypeParameter).symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.Const)) ||
1406114066
type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, t => isConstTypeVariable(t, depth)) ||
1406214067
type.flags & TypeFlags.IndexedAccess && isConstTypeVariable((type as IndexedAccessType).objectType, depth + 1) ||
1406314068
type.flags & TypeFlags.Conditional && isConstTypeVariable(getConstraintOfConditionalType(type as ConditionalType), depth + 1) ||
1406414069
type.flags & TypeFlags.Substitution && isConstTypeVariable((type as SubstitutionType).baseType, depth) ||
14070+
getObjectFlags(type) & ObjectFlags.Mapped && isConstMappedType(type as MappedType, depth) ||
1406514071
isGenericTupleType(type) && findIndex(getElementTypes(type), (t, i) => !!(type.target.elementFlags[i] & ElementFlags.Variadic) && isConstTypeVariable(t, depth)) >= 0
1406614072
));
1406714073
}

tests/baselines/reference/typeParameterConstModifiers.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,8 @@ const thingMapped = <const O extends Record<string, any>>(o: NotEmptyMapped<O>)
402402
>o : NotEmptyMapped<O>
403403

404404
const tMapped = thingMapped({ foo: '' }); // { foo: "" }
405-
>tMapped : { foo: ""; }
406-
>thingMapped({ foo: '' }) : { foo: ""; }
405+
>tMapped : { readonly foo: ""; }
406+
>thingMapped({ foo: '' }) : { readonly foo: ""; }
407407
>thingMapped : <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => NotEmptyMapped<O>
408408
>{ foo: '' } : { foo: ""; }
409409
>foo : ""
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
//// [tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiersReverseMappedTypes.ts] ////
2+
3+
=== typeParameterConstModifiersReverseMappedTypes.ts ===
4+
declare function test1<const T>(obj: {
5+
>test1 : Symbol(test1, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 0, 0))
6+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 0, 23))
7+
>obj : Symbol(obj, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 0, 32))
8+
9+
[K in keyof T]: T[K];
10+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 1, 3))
11+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 0, 23))
12+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 0, 23))
13+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 1, 3))
14+
15+
}): [T, typeof obj];
16+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 0, 23))
17+
>obj : Symbol(obj, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 0, 32))
18+
19+
const result1 = test1({
20+
>result1 : Symbol(result1, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 4, 5))
21+
>test1 : Symbol(test1, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 0, 0))
22+
23+
prop: "foo",
24+
>prop : Symbol(prop, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 4, 23))
25+
26+
nested: {
27+
>nested : Symbol(nested, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 5, 14))
28+
29+
nestedProp: "bar",
30+
>nestedProp : Symbol(nestedProp, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 6, 11))
31+
32+
},
33+
});
34+
35+
declare function test2<const T>(obj: {
36+
>test2 : Symbol(test2, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 9, 3))
37+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 11, 23))
38+
>obj : Symbol(obj, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 11, 32))
39+
40+
readonly [K in keyof T]: T[K];
41+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 12, 12))
42+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 11, 23))
43+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 11, 23))
44+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 12, 12))
45+
46+
}): [T, typeof obj];
47+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 11, 23))
48+
>obj : Symbol(obj, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 11, 32))
49+
50+
const result2 = test2({
51+
>result2 : Symbol(result2, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 15, 5))
52+
>test2 : Symbol(test2, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 9, 3))
53+
54+
prop: "foo",
55+
>prop : Symbol(prop, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 15, 23))
56+
57+
nested: {
58+
>nested : Symbol(nested, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 16, 14))
59+
60+
nestedProp: "bar",
61+
>nestedProp : Symbol(nestedProp, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 17, 11))
62+
63+
},
64+
});
65+
66+
declare function test3<const T>(obj: {
67+
>test3 : Symbol(test3, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 20, 3))
68+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 22, 23))
69+
>obj : Symbol(obj, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 22, 32))
70+
71+
-readonly [K in keyof T]: T[K];
72+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 23, 13))
73+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 22, 23))
74+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 22, 23))
75+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 23, 13))
76+
77+
}): [T, typeof obj];
78+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 22, 23))
79+
>obj : Symbol(obj, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 22, 32))
80+
81+
const result3 = test3({
82+
>result3 : Symbol(result3, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 26, 5))
83+
>test3 : Symbol(test3, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 20, 3))
84+
85+
prop: "foo",
86+
>prop : Symbol(prop, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 26, 23))
87+
88+
nested: {
89+
>nested : Symbol(nested, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 27, 14))
90+
91+
nestedProp: "bar",
92+
>nestedProp : Symbol(nestedProp, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 28, 11))
93+
94+
},
95+
});
96+
97+
declare function test4<const T extends readonly unknown[]>(arr: {
98+
>test4 : Symbol(test4, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 31, 3))
99+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 33, 23))
100+
>arr : Symbol(arr, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 33, 59))
101+
102+
[K in keyof T]: T[K];
103+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 34, 3))
104+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 33, 23))
105+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 33, 23))
106+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 34, 3))
107+
108+
}): T;
109+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 33, 23))
110+
111+
const result4 = test4(["1", 2]);
112+
>result4 : Symbol(result4, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 37, 5))
113+
>test4 : Symbol(test4, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 31, 3))
114+
115+
declare function test5<const T extends readonly unknown[]>(
116+
>test5 : Symbol(test5, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 37, 32))
117+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 39, 23))
118+
119+
...args: {
120+
>args : Symbol(args, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 39, 59))
121+
122+
[K in keyof T]: T[K];
123+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 41, 5))
124+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 39, 23))
125+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 39, 23))
126+
>K : Symbol(K, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 41, 5))
127+
}
128+
): T;
129+
>T : Symbol(T, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 39, 23))
130+
131+
const result5 = test5({ a: "foo" });
132+
>result5 : Symbol(result5, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 45, 5))
133+
>test5 : Symbol(test5, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 37, 32))
134+
>a : Symbol(a, Decl(typeParameterConstModifiersReverseMappedTypes.ts, 45, 23))
135+
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//// [tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiersReverseMappedTypes.ts] ////
2+
3+
=== typeParameterConstModifiersReverseMappedTypes.ts ===
4+
declare function test1<const T>(obj: {
5+
>test1 : <const T>(obj: { [K in keyof T]: T[K]; }) => [T, typeof obj]
6+
>obj : { [K in keyof T]: T[K]; }
7+
8+
[K in keyof T]: T[K];
9+
}): [T, typeof obj];
10+
>obj : { [K in keyof T]: T[K]; }
11+
12+
const result1 = test1({
13+
>result1 : [{ readonly prop: "foo"; readonly nested: { readonly nestedProp: "bar"; }; }, { readonly prop: "foo"; readonly nested: { readonly nestedProp: "bar"; }; }]
14+
>test1({ prop: "foo", nested: { nestedProp: "bar", },}) : [{ readonly prop: "foo"; readonly nested: { readonly nestedProp: "bar"; }; }, { readonly prop: "foo"; readonly nested: { readonly nestedProp: "bar"; }; }]
15+
>test1 : <const T>(obj: { [K in keyof T]: T[K]; }) => [T, { [K in keyof T]: T[K]; }]
16+
>{ prop: "foo", nested: { nestedProp: "bar", },} : { prop: "foo"; nested: { nestedProp: "bar"; }; }
17+
18+
prop: "foo",
19+
>prop : "foo"
20+
>"foo" : "foo"
21+
22+
nested: {
23+
>nested : { nestedProp: "bar"; }
24+
>{ nestedProp: "bar", } : { nestedProp: "bar"; }
25+
26+
nestedProp: "bar",
27+
>nestedProp : "bar"
28+
>"bar" : "bar"
29+
30+
},
31+
});
32+
33+
declare function test2<const T>(obj: {
34+
>test2 : <const T>(obj: { readonly [K in keyof T]: T[K]; }) => [T, typeof obj]
35+
>obj : { readonly [K in keyof T]: T[K]; }
36+
37+
readonly [K in keyof T]: T[K];
38+
}): [T, typeof obj];
39+
>obj : { readonly [K in keyof T]: T[K]; }
40+
41+
const result2 = test2({
42+
>result2 : [{ prop: "foo"; nested: { readonly nestedProp: "bar"; }; }, { readonly prop: "foo"; readonly nested: { readonly nestedProp: "bar"; }; }]
43+
>test2({ prop: "foo", nested: { nestedProp: "bar", },}) : [{ prop: "foo"; nested: { readonly nestedProp: "bar"; }; }, { readonly prop: "foo"; readonly nested: { readonly nestedProp: "bar"; }; }]
44+
>test2 : <const T>(obj: { readonly [K in keyof T]: T[K]; }) => [T, { readonly [K in keyof T]: T[K]; }]
45+
>{ prop: "foo", nested: { nestedProp: "bar", },} : { prop: "foo"; nested: { nestedProp: "bar"; }; }
46+
47+
prop: "foo",
48+
>prop : "foo"
49+
>"foo" : "foo"
50+
51+
nested: {
52+
>nested : { nestedProp: "bar"; }
53+
>{ nestedProp: "bar", } : { nestedProp: "bar"; }
54+
55+
nestedProp: "bar",
56+
>nestedProp : "bar"
57+
>"bar" : "bar"
58+
59+
},
60+
});
61+
62+
declare function test3<const T>(obj: {
63+
>test3 : <const T>(obj: { -readonly [K in keyof T]: T[K]; }) => [T, typeof obj]
64+
>obj : { -readonly [K in keyof T]: T[K]; }
65+
66+
-readonly [K in keyof T]: T[K];
67+
}): [T, typeof obj];
68+
>obj : { -readonly [K in keyof T]: T[K]; }
69+
70+
const result3 = test3({
71+
>result3 : [{ readonly prop: "foo"; readonly nested: { readonly nestedProp: "bar"; }; }, { prop: "foo"; nested: { readonly nestedProp: "bar"; }; }]
72+
>test3({ prop: "foo", nested: { nestedProp: "bar", },}) : [{ readonly prop: "foo"; readonly nested: { readonly nestedProp: "bar"; }; }, { prop: "foo"; nested: { readonly nestedProp: "bar"; }; }]
73+
>test3 : <const T>(obj: { -readonly [K in keyof T]: T[K]; }) => [T, { -readonly [K in keyof T]: T[K]; }]
74+
>{ prop: "foo", nested: { nestedProp: "bar", },} : { prop: "foo"; nested: { nestedProp: "bar"; }; }
75+
76+
prop: "foo",
77+
>prop : "foo"
78+
>"foo" : "foo"
79+
80+
nested: {
81+
>nested : { nestedProp: "bar"; }
82+
>{ nestedProp: "bar", } : { nestedProp: "bar"; }
83+
84+
nestedProp: "bar",
85+
>nestedProp : "bar"
86+
>"bar" : "bar"
87+
88+
},
89+
});
90+
91+
declare function test4<const T extends readonly unknown[]>(arr: {
92+
>test4 : <const T extends readonly unknown[]>(arr: { [K in keyof T]: T[K]; }) => T
93+
>arr : { [K in keyof T]: T[K]; }
94+
95+
[K in keyof T]: T[K];
96+
}): T;
97+
98+
const result4 = test4(["1", 2]);
99+
>result4 : readonly ["1", 2]
100+
>test4(["1", 2]) : readonly ["1", 2]
101+
>test4 : <const T extends readonly unknown[]>(arr: { [K in keyof T]: T[K]; }) => T
102+
>["1", 2] : ["1", 2]
103+
>"1" : "1"
104+
>2 : 2
105+
106+
declare function test5<const T extends readonly unknown[]>(
107+
>test5 : <const T extends readonly unknown[]>(...args: { [K in keyof T]: T[K]; }) => T
108+
109+
...args: {
110+
>args : { [K in keyof T]: T[K]; }
111+
112+
[K in keyof T]: T[K];
113+
}
114+
): T;
115+
116+
const result5 = test5({ a: "foo" });
117+
>result5 : readonly [{ readonly a: "foo"; }]
118+
>test5({ a: "foo" }) : readonly [{ readonly a: "foo"; }]
119+
>test5 : <const T extends readonly unknown[]>(...args: { [K in keyof T]: T[K]; }) => T
120+
>{ a: "foo" } : { a: "foo"; }
121+
>a : "foo"
122+
>"foo" : "foo"
123+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
declare function test1<const T>(obj: {
5+
[K in keyof T]: T[K];
6+
}): [T, typeof obj];
7+
8+
const result1 = test1({
9+
prop: "foo",
10+
nested: {
11+
nestedProp: "bar",
12+
},
13+
});
14+
15+
declare function test2<const T>(obj: {
16+
readonly [K in keyof T]: T[K];
17+
}): [T, typeof obj];
18+
19+
const result2 = test2({
20+
prop: "foo",
21+
nested: {
22+
nestedProp: "bar",
23+
},
24+
});
25+
26+
declare function test3<const T>(obj: {
27+
-readonly [K in keyof T]: T[K];
28+
}): [T, typeof obj];
29+
30+
const result3 = test3({
31+
prop: "foo",
32+
nested: {
33+
nestedProp: "bar",
34+
},
35+
});
36+
37+
declare function test4<const T extends readonly unknown[]>(arr: {
38+
[K in keyof T]: T[K];
39+
}): T;
40+
41+
const result4 = test4(["1", 2]);
42+
43+
declare function test5<const T extends readonly unknown[]>(
44+
...args: {
45+
[K in keyof T]: T[K];
46+
}
47+
): T;
48+
49+
const result5 = test5({ a: "foo" });

0 commit comments

Comments
 (0)