Skip to content

Commit c25d033

Browse files
committed
support type inference of object non-literal keys
1 parent 9c3b41d commit c25d033

6 files changed

+596
-0
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26387,6 +26387,12 @@ namespace ts {
2638726387
}
2638826388
if (element.name) {
2638926389
const nameType = getLiteralTypeFromPropertyName(element.name);
26390+
if (nameType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
26391+
const propType = getTypeOfPropertyOfContextualType(type, escapeLeadingUnderscores((nameType as LiteralType).value.toString()), nameType);
26392+
if (propType) {
26393+
return propType;
26394+
}
26395+
}
2639026396
// We avoid calling getApplicableIndexInfo here because it performs potentially expensive intersection reduction.
2639126397
return mapType(type, t => findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType)?.type, /*noReductions*/ true);
2639226398
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
tests/cases/compiler/contextualTypeInObjectProperty.ts(19,17): error TS7006: Parameter 'keyC' implicitly has an 'any' type.
2+
tests/cases/compiler/contextualTypeInObjectProperty.ts(24,3): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
3+
tests/cases/compiler/contextualTypeInObjectProperty.ts(24,11): error TS7006: Parameter 'keyC' implicitly has an 'any' type.
4+
5+
6+
==== tests/cases/compiler/contextualTypeInObjectProperty.ts (3 errors) ====
7+
type Shape = {
8+
"a"?: (a: "a") => "a";
9+
"b"?: (b: "b") => "b";
10+
"c"?: (c: "c") => "c";
11+
};
12+
13+
const getC = () => "c" as const;
14+
15+
export const obj: Shape = {
16+
["a"]: keyA => keyA,
17+
["b" as "b"]: keyB => keyB,
18+
[getC()]: keyC => keyC,
19+
};
20+
21+
22+
const getUnion = () => "b" as "b" | "c";
23+
24+
export const unionType: Shape = {
25+
[getUnion()]: keyC => keyC, // Error
26+
~~~~
27+
!!! error TS7006: Parameter 'keyC' implicitly has an 'any' type.
28+
};
29+
30+
31+
export const func: Shape = {
32+
[getC]: keyC => keyC, // Error
33+
~~~~~~
34+
!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
35+
~~~~
36+
!!! error TS7006: Parameter 'keyC' implicitly has an 'any' type.
37+
};
38+
39+
const generic: {
40+
c: <T>(arg: T) => T;
41+
} = {
42+
[getC()]: keyC => keyC,
43+
};
44+
45+
const thisType = {
46+
[getC()]: function() {
47+
this.c();
48+
}
49+
};
50+
51+
52+
declare function f<T extends object>(data: T, handlers: { [P in keyof T]: (value: T[P], prop: P) => void; }): void;
53+
f({ data: 0 }, {
54+
[(() => 'data' as const)()](value, key) {
55+
56+
},
57+
});
58+
59+
60+
enum Keys {
61+
FIRST,
62+
SECOND
63+
}
64+
65+
const obj2: {
66+
[key in Keys]: [string, string]
67+
} = {
68+
[Keys.FIRST]: ['1', '2'],
69+
[Keys['SECOND']]: ['3', '4']
70+
}
71+
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//// [contextualTypeInObjectProperty.ts]
2+
type Shape = {
3+
"a"?: (a: "a") => "a";
4+
"b"?: (b: "b") => "b";
5+
"c"?: (c: "c") => "c";
6+
};
7+
8+
const getC = () => "c" as const;
9+
10+
export const obj: Shape = {
11+
["a"]: keyA => keyA,
12+
["b" as "b"]: keyB => keyB,
13+
[getC()]: keyC => keyC,
14+
};
15+
16+
17+
const getUnion = () => "b" as "b" | "c";
18+
19+
export const unionType: Shape = {
20+
[getUnion()]: keyC => keyC, // Error
21+
};
22+
23+
24+
export const func: Shape = {
25+
[getC]: keyC => keyC, // Error
26+
};
27+
28+
const generic: {
29+
c: <T>(arg: T) => T;
30+
} = {
31+
[getC()]: keyC => keyC,
32+
};
33+
34+
const thisType = {
35+
[getC()]: function() {
36+
this.c();
37+
}
38+
};
39+
40+
41+
declare function f<T extends object>(data: T, handlers: { [P in keyof T]: (value: T[P], prop: P) => void; }): void;
42+
f({ data: 0 }, {
43+
[(() => 'data' as const)()](value, key) {
44+
45+
},
46+
});
47+
48+
49+
enum Keys {
50+
FIRST,
51+
SECOND
52+
}
53+
54+
const obj2: {
55+
[key in Keys]: [string, string]
56+
} = {
57+
[Keys.FIRST]: ['1', '2'],
58+
[Keys['SECOND']]: ['3', '4']
59+
}
60+
61+
62+
//// [contextualTypeInObjectProperty.js]
63+
"use strict";
64+
var _a, _b, _c, _d, _e, _f, _g;
65+
exports.__esModule = true;
66+
exports.func = exports.unionType = exports.obj = void 0;
67+
var getC = function () { return "c"; };
68+
exports.obj = (_a = {},
69+
_a["a"] = function (keyA) { return keyA; },
70+
_a["b"] = function (keyB) { return keyB; },
71+
_a[getC()] = function (keyC) { return keyC; },
72+
_a);
73+
var getUnion = function () { return "b"; };
74+
exports.unionType = (_b = {},
75+
_b[getUnion()] = function (keyC) { return keyC; },
76+
_b);
77+
exports.func = (_c = {},
78+
_c[getC] = function (keyC) { return keyC; },
79+
_c);
80+
var generic = (_d = {},
81+
_d[getC()] = function (keyC) { return keyC; },
82+
_d);
83+
var thisType = (_e = {},
84+
_e[getC()] = function () {
85+
this.c();
86+
},
87+
_e);
88+
f({ data: 0 }, (_f = {},
89+
_f[(function () { return 'data'; })()] = function (value, key) {
90+
},
91+
_f));
92+
var Keys;
93+
(function (Keys) {
94+
Keys[Keys["FIRST"] = 0] = "FIRST";
95+
Keys[Keys["SECOND"] = 1] = "SECOND";
96+
})(Keys || (Keys = {}));
97+
var obj2 = (_g = {},
98+
_g[Keys.FIRST] = ['1', '2'],
99+
_g[Keys['SECOND']] = ['3', '4'],
100+
_g);
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
=== tests/cases/compiler/contextualTypeInObjectProperty.ts ===
2+
type Shape = {
3+
>Shape : Symbol(Shape, Decl(contextualTypeInObjectProperty.ts, 0, 0))
4+
5+
"a"?: (a: "a") => "a";
6+
>"a" : Symbol("a", Decl(contextualTypeInObjectProperty.ts, 0, 14))
7+
>a : Symbol(a, Decl(contextualTypeInObjectProperty.ts, 1, 11))
8+
9+
"b"?: (b: "b") => "b";
10+
>"b" : Symbol("b", Decl(contextualTypeInObjectProperty.ts, 1, 26))
11+
>b : Symbol(b, Decl(contextualTypeInObjectProperty.ts, 2, 11))
12+
13+
"c"?: (c: "c") => "c";
14+
>"c" : Symbol("c", Decl(contextualTypeInObjectProperty.ts, 2, 26))
15+
>c : Symbol(c, Decl(contextualTypeInObjectProperty.ts, 3, 11))
16+
17+
};
18+
19+
const getC = () => "c" as const;
20+
>getC : Symbol(getC, Decl(contextualTypeInObjectProperty.ts, 6, 5))
21+
>const : Symbol(const)
22+
23+
export const obj: Shape = {
24+
>obj : Symbol(obj, Decl(contextualTypeInObjectProperty.ts, 8, 12))
25+
>Shape : Symbol(Shape, Decl(contextualTypeInObjectProperty.ts, 0, 0))
26+
27+
["a"]: keyA => keyA,
28+
>["a"] : Symbol(["a"], Decl(contextualTypeInObjectProperty.ts, 8, 27))
29+
>"a" : Symbol(["a"], Decl(contextualTypeInObjectProperty.ts, 8, 27))
30+
>keyA : Symbol(keyA, Decl(contextualTypeInObjectProperty.ts, 9, 8))
31+
>keyA : Symbol(keyA, Decl(contextualTypeInObjectProperty.ts, 9, 8))
32+
33+
["b" as "b"]: keyB => keyB,
34+
>["b" as "b"] : Symbol(["b" as "b"], Decl(contextualTypeInObjectProperty.ts, 9, 22))
35+
>keyB : Symbol(keyB, Decl(contextualTypeInObjectProperty.ts, 10, 15))
36+
>keyB : Symbol(keyB, Decl(contextualTypeInObjectProperty.ts, 10, 15))
37+
38+
[getC()]: keyC => keyC,
39+
>[getC()] : Symbol([getC()], Decl(contextualTypeInObjectProperty.ts, 10, 29))
40+
>getC : Symbol(getC, Decl(contextualTypeInObjectProperty.ts, 6, 5))
41+
>keyC : Symbol(keyC, Decl(contextualTypeInObjectProperty.ts, 11, 11))
42+
>keyC : Symbol(keyC, Decl(contextualTypeInObjectProperty.ts, 11, 11))
43+
44+
};
45+
46+
47+
const getUnion = () => "b" as "b" | "c";
48+
>getUnion : Symbol(getUnion, Decl(contextualTypeInObjectProperty.ts, 15, 5))
49+
50+
export const unionType: Shape = {
51+
>unionType : Symbol(unionType, Decl(contextualTypeInObjectProperty.ts, 17, 12))
52+
>Shape : Symbol(Shape, Decl(contextualTypeInObjectProperty.ts, 0, 0))
53+
54+
[getUnion()]: keyC => keyC, // Error
55+
>[getUnion()] : Symbol([getUnion()], Decl(contextualTypeInObjectProperty.ts, 17, 33))
56+
>getUnion : Symbol(getUnion, Decl(contextualTypeInObjectProperty.ts, 15, 5))
57+
>keyC : Symbol(keyC, Decl(contextualTypeInObjectProperty.ts, 18, 15))
58+
>keyC : Symbol(keyC, Decl(contextualTypeInObjectProperty.ts, 18, 15))
59+
60+
};
61+
62+
63+
export const func: Shape = {
64+
>func : Symbol(func, Decl(contextualTypeInObjectProperty.ts, 22, 12))
65+
>Shape : Symbol(Shape, Decl(contextualTypeInObjectProperty.ts, 0, 0))
66+
67+
[getC]: keyC => keyC, // Error
68+
>[getC] : Symbol([getC], Decl(contextualTypeInObjectProperty.ts, 22, 28))
69+
>getC : Symbol(getC, Decl(contextualTypeInObjectProperty.ts, 6, 5))
70+
>keyC : Symbol(keyC, Decl(contextualTypeInObjectProperty.ts, 23, 9))
71+
>keyC : Symbol(keyC, Decl(contextualTypeInObjectProperty.ts, 23, 9))
72+
73+
};
74+
75+
const generic: {
76+
>generic : Symbol(generic, Decl(contextualTypeInObjectProperty.ts, 26, 5))
77+
78+
c: <T>(arg: T) => T;
79+
>c : Symbol(c, Decl(contextualTypeInObjectProperty.ts, 26, 16))
80+
>T : Symbol(T, Decl(contextualTypeInObjectProperty.ts, 27, 6))
81+
>arg : Symbol(arg, Decl(contextualTypeInObjectProperty.ts, 27, 9))
82+
>T : Symbol(T, Decl(contextualTypeInObjectProperty.ts, 27, 6))
83+
>T : Symbol(T, Decl(contextualTypeInObjectProperty.ts, 27, 6))
84+
85+
} = {
86+
[getC()]: keyC => keyC,
87+
>[getC()] : Symbol([getC()], Decl(contextualTypeInObjectProperty.ts, 28, 5))
88+
>getC : Symbol(getC, Decl(contextualTypeInObjectProperty.ts, 6, 5))
89+
>keyC : Symbol(keyC, Decl(contextualTypeInObjectProperty.ts, 29, 11))
90+
>keyC : Symbol(keyC, Decl(contextualTypeInObjectProperty.ts, 29, 11))
91+
92+
};
93+
94+
const thisType = {
95+
>thisType : Symbol(thisType, Decl(contextualTypeInObjectProperty.ts, 32, 5))
96+
97+
[getC()]: function() {
98+
>[getC()] : Symbol([getC()], Decl(contextualTypeInObjectProperty.ts, 32, 18))
99+
>getC : Symbol(getC, Decl(contextualTypeInObjectProperty.ts, 6, 5))
100+
101+
this.c();
102+
>this.c : Symbol([getC()], Decl(contextualTypeInObjectProperty.ts, 32, 18))
103+
>this : Symbol(thisType, Decl(contextualTypeInObjectProperty.ts, 32, 16))
104+
>c : Symbol([getC()], Decl(contextualTypeInObjectProperty.ts, 32, 18))
105+
}
106+
};
107+
108+
109+
declare function f<T extends object>(data: T, handlers: { [P in keyof T]: (value: T[P], prop: P) => void; }): void;
110+
>f : Symbol(f, Decl(contextualTypeInObjectProperty.ts, 36, 2))
111+
>T : Symbol(T, Decl(contextualTypeInObjectProperty.ts, 39, 19))
112+
>data : Symbol(data, Decl(contextualTypeInObjectProperty.ts, 39, 37))
113+
>T : Symbol(T, Decl(contextualTypeInObjectProperty.ts, 39, 19))
114+
>handlers : Symbol(handlers, Decl(contextualTypeInObjectProperty.ts, 39, 45))
115+
>P : Symbol(P, Decl(contextualTypeInObjectProperty.ts, 39, 59))
116+
>T : Symbol(T, Decl(contextualTypeInObjectProperty.ts, 39, 19))
117+
>value : Symbol(value, Decl(contextualTypeInObjectProperty.ts, 39, 75))
118+
>T : Symbol(T, Decl(contextualTypeInObjectProperty.ts, 39, 19))
119+
>P : Symbol(P, Decl(contextualTypeInObjectProperty.ts, 39, 59))
120+
>prop : Symbol(prop, Decl(contextualTypeInObjectProperty.ts, 39, 87))
121+
>P : Symbol(P, Decl(contextualTypeInObjectProperty.ts, 39, 59))
122+
123+
f({ data: 0 }, {
124+
>f : Symbol(f, Decl(contextualTypeInObjectProperty.ts, 36, 2))
125+
>data : Symbol(data, Decl(contextualTypeInObjectProperty.ts, 40, 3))
126+
127+
[(() => 'data' as const)()](value, key) {
128+
>[(() => 'data' as const)()] : Symbol([(() => 'data' as const)()], Decl(contextualTypeInObjectProperty.ts, 40, 16))
129+
>const : Symbol(const)
130+
>value : Symbol(value, Decl(contextualTypeInObjectProperty.ts, 41, 30))
131+
>key : Symbol(key, Decl(contextualTypeInObjectProperty.ts, 41, 36))
132+
133+
},
134+
});
135+
136+
137+
enum Keys {
138+
>Keys : Symbol(Keys, Decl(contextualTypeInObjectProperty.ts, 44, 3))
139+
140+
FIRST,
141+
>FIRST : Symbol(Keys.FIRST, Decl(contextualTypeInObjectProperty.ts, 47, 11))
142+
143+
SECOND
144+
>SECOND : Symbol(Keys.SECOND, Decl(contextualTypeInObjectProperty.ts, 48, 8))
145+
}
146+
147+
const obj2: {
148+
>obj2 : Symbol(obj2, Decl(contextualTypeInObjectProperty.ts, 52, 5))
149+
150+
[key in Keys]: [string, string]
151+
>key : Symbol(key, Decl(contextualTypeInObjectProperty.ts, 53, 3))
152+
>Keys : Symbol(Keys, Decl(contextualTypeInObjectProperty.ts, 44, 3))
153+
154+
} = {
155+
[Keys.FIRST]: ['1', '2'],
156+
>[Keys.FIRST] : Symbol([Keys.FIRST], Decl(contextualTypeInObjectProperty.ts, 54, 5))
157+
>Keys.FIRST : Symbol(Keys.FIRST, Decl(contextualTypeInObjectProperty.ts, 47, 11))
158+
>Keys : Symbol(Keys, Decl(contextualTypeInObjectProperty.ts, 44, 3))
159+
>FIRST : Symbol(Keys.FIRST, Decl(contextualTypeInObjectProperty.ts, 47, 11))
160+
161+
[Keys['SECOND']]: ['3', '4']
162+
>[Keys['SECOND']] : Symbol([Keys['SECOND']], Decl(contextualTypeInObjectProperty.ts, 55, 27))
163+
>Keys : Symbol(Keys, Decl(contextualTypeInObjectProperty.ts, 44, 3))
164+
>'SECOND' : Symbol(Keys.SECOND, Decl(contextualTypeInObjectProperty.ts, 48, 8))
165+
}
166+

0 commit comments

Comments
 (0)