Skip to content

Commit 58ee54c

Browse files
authored
Resolve contextual computed properties with non-bindable names (#51915)
1 parent 73a98ae commit 58ee54c

File tree

4 files changed

+243
-0
lines changed

4 files changed

+243
-0
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28997,6 +28997,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2899728997
const symbol = getSymbolOfDeclaration(element);
2899828998
return getTypeOfPropertyOfContextualType(type, symbol.escapedName, getSymbolLinks(symbol).nameType);
2899928999
}
29000+
if (hasDynamicName(element)) {
29001+
const name = getNameOfDeclaration(element);
29002+
if (name && isComputedPropertyName(name)) {
29003+
const exprType = checkExpression(name.expression);
29004+
const propType = isTypeUsableAsPropertyName(exprType) && getTypeOfPropertyOfContextualType(type, getPropertyNameFromType(exprType));
29005+
if (propType) {
29006+
return propType;
29007+
}
29008+
}
29009+
}
2900029010
if (element.name) {
2900129011
const nameType = getLiteralTypeFromPropertyName(element.name);
2900229012
// We avoid calling getApplicableIndexInfo here because it performs potentially expensive intersection reduction.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
=== tests/cases/compiler/contextualComputedNonBindablePropertyType.ts ===
2+
// repro #51906
3+
4+
declare function testD(): "d";
5+
>testD : Symbol(testD, Decl(contextualComputedNonBindablePropertyType.ts, 0, 0))
6+
7+
declare function forceMatch<T>(matched: {
8+
>forceMatch : Symbol(forceMatch, Decl(contextualComputedNonBindablePropertyType.ts, 2, 30))
9+
>T : Symbol(T, Decl(contextualComputedNonBindablePropertyType.ts, 4, 28))
10+
>matched : Symbol(matched, Decl(contextualComputedNonBindablePropertyType.ts, 4, 31))
11+
12+
[key in keyof T]: key;
13+
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 5, 3))
14+
>T : Symbol(T, Decl(contextualComputedNonBindablePropertyType.ts, 4, 28))
15+
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 5, 3))
16+
17+
}): void;
18+
19+
forceMatch({
20+
>forceMatch : Symbol(forceMatch, Decl(contextualComputedNonBindablePropertyType.ts, 2, 30))
21+
22+
[testD()]: "d",
23+
>[testD()] : Symbol([testD()], Decl(contextualComputedNonBindablePropertyType.ts, 8, 12))
24+
>testD : Symbol(testD, Decl(contextualComputedNonBindablePropertyType.ts, 0, 0))
25+
26+
});
27+
28+
declare function forceMatch2<T>(matched: {
29+
>forceMatch2 : Symbol(forceMatch2, Decl(contextualComputedNonBindablePropertyType.ts, 10, 3))
30+
>T : Symbol(T, Decl(contextualComputedNonBindablePropertyType.ts, 12, 29))
31+
>matched : Symbol(matched, Decl(contextualComputedNonBindablePropertyType.ts, 12, 32))
32+
33+
[key in keyof T]: ({ key }: { key: key }) => void;
34+
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 13, 3))
35+
>T : Symbol(T, Decl(contextualComputedNonBindablePropertyType.ts, 12, 29))
36+
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 13, 22))
37+
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 13, 31))
38+
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 13, 3))
39+
40+
}): void;
41+
42+
forceMatch2({
43+
>forceMatch2 : Symbol(forceMatch2, Decl(contextualComputedNonBindablePropertyType.ts, 10, 3))
44+
45+
[testD()]: ({ key }) => {},
46+
>[testD()] : Symbol([testD()], Decl(contextualComputedNonBindablePropertyType.ts, 16, 13))
47+
>testD : Symbol(testD, Decl(contextualComputedNonBindablePropertyType.ts, 0, 0))
48+
>key : Symbol(key, Decl(contextualComputedNonBindablePropertyType.ts, 17, 15))
49+
50+
});
51+
52+
// repro #52954
53+
54+
type Original = { foo: 'expects a string literal', baz: boolean, bar: number }
55+
>Original : Symbol(Original, Decl(contextualComputedNonBindablePropertyType.ts, 18, 3))
56+
>foo : Symbol(foo, Decl(contextualComputedNonBindablePropertyType.ts, 22, 17))
57+
>baz : Symbol(baz, Decl(contextualComputedNonBindablePropertyType.ts, 22, 50))
58+
>bar : Symbol(bar, Decl(contextualComputedNonBindablePropertyType.ts, 22, 64))
59+
60+
type Mapped = {
61+
>Mapped : Symbol(Mapped, Decl(contextualComputedNonBindablePropertyType.ts, 22, 78))
62+
63+
[prop in keyof Original]: (arg: Original[prop]) => Original[prop]
64+
>prop : Symbol(prop, Decl(contextualComputedNonBindablePropertyType.ts, 25, 3))
65+
>Original : Symbol(Original, Decl(contextualComputedNonBindablePropertyType.ts, 18, 3))
66+
>arg : Symbol(arg, Decl(contextualComputedNonBindablePropertyType.ts, 25, 29))
67+
>Original : Symbol(Original, Decl(contextualComputedNonBindablePropertyType.ts, 18, 3))
68+
>prop : Symbol(prop, Decl(contextualComputedNonBindablePropertyType.ts, 25, 3))
69+
>Original : Symbol(Original, Decl(contextualComputedNonBindablePropertyType.ts, 18, 3))
70+
>prop : Symbol(prop, Decl(contextualComputedNonBindablePropertyType.ts, 25, 3))
71+
}
72+
73+
const propSelector = <propName extends string>(propName: propName): propName => propName;
74+
>propSelector : Symbol(propSelector, Decl(contextualComputedNonBindablePropertyType.ts, 28, 5))
75+
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
76+
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
77+
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
78+
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
79+
>propName : Symbol(propName, Decl(contextualComputedNonBindablePropertyType.ts, 28, 23), Decl(contextualComputedNonBindablePropertyType.ts, 28, 48))
80+
81+
const unexpectedlyFailingExample: Mapped = {
82+
>unexpectedlyFailingExample : Symbol(unexpectedlyFailingExample, Decl(contextualComputedNonBindablePropertyType.ts, 30, 5))
83+
>Mapped : Symbol(Mapped, Decl(contextualComputedNonBindablePropertyType.ts, 22, 78))
84+
85+
foo: (arg) => 'expects a string literal',
86+
>foo : Symbol(foo, Decl(contextualComputedNonBindablePropertyType.ts, 30, 44))
87+
>arg : Symbol(arg, Decl(contextualComputedNonBindablePropertyType.ts, 31, 8))
88+
89+
baz: (arg) => true,
90+
>baz : Symbol(baz, Decl(contextualComputedNonBindablePropertyType.ts, 31, 43))
91+
>arg : Symbol(arg, Decl(contextualComputedNonBindablePropertyType.ts, 32, 8))
92+
93+
[propSelector('bar')]: (arg) => 51345
94+
>[propSelector('bar')] : Symbol([propSelector('bar')], Decl(contextualComputedNonBindablePropertyType.ts, 32, 21))
95+
>propSelector : Symbol(propSelector, Decl(contextualComputedNonBindablePropertyType.ts, 28, 5))
96+
>arg : Symbol(arg, Decl(contextualComputedNonBindablePropertyType.ts, 33, 26))
97+
}
98+
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
=== tests/cases/compiler/contextualComputedNonBindablePropertyType.ts ===
2+
// repro #51906
3+
4+
declare function testD(): "d";
5+
>testD : () => "d"
6+
7+
declare function forceMatch<T>(matched: {
8+
>forceMatch : <T>(matched: { [key in keyof T]: key; }) => void
9+
>matched : { [key in keyof T]: key; }
10+
11+
[key in keyof T]: key;
12+
}): void;
13+
14+
forceMatch({
15+
>forceMatch({ [testD()]: "d",}) : void
16+
>forceMatch : <T>(matched: { [key in keyof T]: key; }) => void
17+
>{ [testD()]: "d",} : { d: "d"; }
18+
19+
[testD()]: "d",
20+
>[testD()] : "d"
21+
>testD() : "d"
22+
>testD : () => "d"
23+
>"d" : "d"
24+
25+
});
26+
27+
declare function forceMatch2<T>(matched: {
28+
>forceMatch2 : <T>(matched: { [key in keyof T]: ({ key }: { key: key; }) => void; }) => void
29+
>matched : { [key in keyof T]: ({ key }: { key: key; }) => void; }
30+
31+
[key in keyof T]: ({ key }: { key: key }) => void;
32+
>key : key
33+
>key : key
34+
35+
}): void;
36+
37+
forceMatch2({
38+
>forceMatch2({ [testD()]: ({ key }) => {},}) : void
39+
>forceMatch2 : <T>(matched: { [key in keyof T]: ({ key }: { key: key; }) => void; }) => void
40+
>{ [testD()]: ({ key }) => {},} : { d: ({ key }: { key: "d"; }) => void; }
41+
42+
[testD()]: ({ key }) => {},
43+
>[testD()] : ({ key }: { key: "d"; }) => void
44+
>testD() : "d"
45+
>testD : () => "d"
46+
>({ key }) => {} : ({ key }: { key: "d"; }) => void
47+
>key : "d"
48+
49+
});
50+
51+
// repro #52954
52+
53+
type Original = { foo: 'expects a string literal', baz: boolean, bar: number }
54+
>Original : { foo: 'expects a string literal'; baz: boolean; bar: number; }
55+
>foo : "expects a string literal"
56+
>baz : boolean
57+
>bar : number
58+
59+
type Mapped = {
60+
>Mapped : { foo: (arg: "expects a string literal") => "expects a string literal"; baz: (arg: boolean) => boolean; bar: (arg: number) => number; }
61+
62+
[prop in keyof Original]: (arg: Original[prop]) => Original[prop]
63+
>arg : Original[prop]
64+
}
65+
66+
const propSelector = <propName extends string>(propName: propName): propName => propName;
67+
>propSelector : <propName extends string>(propName: propName) => propName
68+
><propName extends string>(propName: propName): propName => propName : <propName extends string>(propName: propName) => propName
69+
>propName : propName
70+
>propName : propName
71+
72+
const unexpectedlyFailingExample: Mapped = {
73+
>unexpectedlyFailingExample : Mapped
74+
>{ foo: (arg) => 'expects a string literal', baz: (arg) => true, [propSelector('bar')]: (arg) => 51345} : { foo: (arg: "expects a string literal") => "expects a string literal"; baz: (arg: boolean) => true; bar: (arg: number) => number; }
75+
76+
foo: (arg) => 'expects a string literal',
77+
>foo : (arg: "expects a string literal") => "expects a string literal"
78+
>(arg) => 'expects a string literal' : (arg: "expects a string literal") => "expects a string literal"
79+
>arg : "expects a string literal"
80+
>'expects a string literal' : "expects a string literal"
81+
82+
baz: (arg) => true,
83+
>baz : (arg: boolean) => true
84+
>(arg) => true : (arg: boolean) => true
85+
>arg : boolean
86+
>true : true
87+
88+
[propSelector('bar')]: (arg) => 51345
89+
>[propSelector('bar')] : (arg: number) => number
90+
>propSelector('bar') : "bar"
91+
>propSelector : <propName extends string>(propName: propName) => propName
92+
>'bar' : "bar"
93+
>(arg) => 51345 : (arg: number) => number
94+
>arg : number
95+
>51345 : 51345
96+
}
97+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// @noEmit: true
2+
// @strict: true
3+
4+
// repro #51906
5+
6+
declare function testD(): "d";
7+
8+
declare function forceMatch<T>(matched: {
9+
[key in keyof T]: key;
10+
}): void;
11+
12+
forceMatch({
13+
[testD()]: "d",
14+
});
15+
16+
declare function forceMatch2<T>(matched: {
17+
[key in keyof T]: ({ key }: { key: key }) => void;
18+
}): void;
19+
20+
forceMatch2({
21+
[testD()]: ({ key }) => {},
22+
});
23+
24+
// repro #52954
25+
26+
type Original = { foo: 'expects a string literal', baz: boolean, bar: number }
27+
28+
type Mapped = {
29+
[prop in keyof Original]: (arg: Original[prop]) => Original[prop]
30+
}
31+
32+
const propSelector = <propName extends string>(propName: propName): propName => propName;
33+
34+
const unexpectedlyFailingExample: Mapped = {
35+
foo: (arg) => 'expects a string literal',
36+
baz: (arg) => true,
37+
[propSelector('bar')]: (arg) => 51345
38+
}

0 commit comments

Comments
 (0)