Skip to content

Commit 15e69ac

Browse files
authored
capture thisArg of optionalChaining in parens (#35494)
Fixes: #35476
1 parent 8a81a67 commit 15e69ac

File tree

4 files changed

+52
-3
lines changed

4 files changed

+52
-3
lines changed

src/compiler/transformers/es2020.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ namespace ts {
2121
return node;
2222
}
2323
switch (node.kind) {
24+
case SyntaxKind.CallExpression: {
25+
const updated = visitNonOptionalCallExpression(node as CallExpression, /*captureThisArg*/ false);
26+
Debug.assertNotNode(updated, isSyntheticReference);
27+
return updated;
28+
}
2429
case SyntaxKind.PropertyAccessExpression:
2530
case SyntaxKind.ElementAccessExpression:
26-
case SyntaxKind.CallExpression:
27-
if (node.flags & NodeFlags.OptionalChain) {
28-
const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false, /*isDelete*/ false);
31+
if (isOptionalChain(node)) {
32+
const updated = visitOptionalExpression(node, /*captureThisArg*/ false, /*isDelete*/ false);
2933
Debug.assertNotNode(updated, isSyntheticReference);
3034
return updated;
3135
}
@@ -94,6 +98,15 @@ namespace ts {
9498
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
9599
return visitOptionalExpression(node, captureThisArg, /*isDelete*/ false);
96100
}
101+
if (isParenthesizedExpression(node.expression) && isOptionalChain(skipParentheses(node.expression))) {
102+
// capture thisArg for calls of parenthesized optional chains like `(foo?.bar)()`
103+
const expression = visitNonOptionalParenthesizedExpression(node.expression, /*captureThisArg*/ true, /*isDelete*/ false);
104+
const args = visitNodes(node.arguments, visitor, isExpression);
105+
if (isSyntheticReference(expression)) {
106+
return setTextRange(factory.createFunctionCallCall(expression.expression, expression.thisArg, args), node);
107+
}
108+
return factory.updateCallExpression(node, expression, /*typeArguments*/ undefined, args);
109+
}
97110
return visitEachChild(node, visitor, context);
98111
}
99112

src/testRunner/unittests/evaluation/optionalCall.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,11 @@ describe("unittests:: evaluation:: optionalCall", () => {
188188
assert.strictEqual(result.output[1], 2);
189189
assert.strictEqual(result.output[2], result.o);
190190
});
191+
it("(o?.f)()", async () => {
192+
const result = evaluator.evaluateTypeScript(`
193+
export const foo = { bar() { return this } };
194+
export const output = (foo?.bar)();
195+
`);
196+
assert.strictEqual(result.output, result.foo);
197+
});
191198
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//// [parentheses.ts]
2+
declare const o1: ((...args: any[]) => number);
3+
declare const o2: { b: (...args: any[]) => number };
4+
declare const o3: { b: ((...args: any[]) => (...args: any[]) => number) };
5+
declare const o4: { b: ((...args: any[]) => { c: (...args: any[]) => number } ) };
6+
7+
(o1)(o1 ?? 1);
8+
(o2?.b)(o1 ?? 1);
9+
(o3?.b())(o1 ?? 1);
10+
(o4?.b().c)(o1 ?? 1);
11+
12+
13+
//// [parentheses.js]
14+
var _a;
15+
(o1)(o1 !== null && o1 !== void 0 ? o1 : 1);
16+
(o2 === null || o2 === void 0 ? void 0 : o2.b).call(o2, o1 !== null && o1 !== void 0 ? o1 : 1);
17+
(o3 === null || o3 === void 0 ? void 0 : o3.b())(o1 !== null && o1 !== void 0 ? o1 : 1);
18+
(o4 === null || o4 === void 0 ? void 0 : (_a = o4.b()).c).call(_a, o1 !== null && o1 !== void 0 ? o1 : 1);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @noTypesAndSymbols: true
2+
3+
declare const o1: ((...args: any[]) => number);
4+
declare const o2: { b: (...args: any[]) => number };
5+
declare const o3: { b: ((...args: any[]) => (...args: any[]) => number) };
6+
declare const o4: { b: ((...args: any[]) => { c: (...args: any[]) => number } ) };
7+
8+
(o1)(o1 ?? 1);
9+
(o2?.b)(o1 ?? 1);
10+
(o3?.b())(o1 ?? 1);
11+
(o4?.b().c)(o1 ?? 1);

0 commit comments

Comments
 (0)