Skip to content

Commit e12664a

Browse files
committed
capture thisArg of optionalChaining in parens
Fixes: #35476
1 parent e8748f8 commit e12664a

File tree

4 files changed

+52
-3
lines changed

4 files changed

+52
-3
lines changed

src/compiler/transformers/esnext.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ namespace ts {
2020
return node;
2121
}
2222
switch (node.kind) {
23+
case SyntaxKind.CallExpression: {
24+
const updated = visitNonOptionalCallExpression(node as CallExpression, /*captureThisArg*/ false);
25+
Debug.assertNotNode(updated, isSyntheticReference);
26+
return updated;
27+
}
2328
case SyntaxKind.PropertyAccessExpression:
2429
case SyntaxKind.ElementAccessExpression:
25-
case SyntaxKind.CallExpression:
26-
if (node.flags & NodeFlags.OptionalChain) {
27-
const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false, /*isDelete*/ false);
30+
if (isOptionalChain(node)) {
31+
const updated = visitOptionalExpression(node, /*captureThisArg*/ false, /*isDelete*/ false);
2832
Debug.assertNotNode(updated, isSyntheticReference);
2933
return updated;
3034
}
@@ -91,6 +95,15 @@ namespace ts {
9195
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
9296
return visitOptionalExpression(node, captureThisArg, /*isDelete*/ false);
9397
}
98+
if (isOptionalChain(skipParentheses(node.expression))) {
99+
// capture thisArg for calls of parenthesized optional chains like `(foo?.bar)()`
100+
const expression = visitNonOptionalParenthesizedExpression(node.expression as ParenthesizedExpression, /*captureThisArg*/ true, /*isDelete*/ false);
101+
const args = visitNodes(node.arguments, visitor, isExpression);
102+
if (isSyntheticReference(expression)) {
103+
return createFunctionCall(expression.expression, expression.thisArg, args, node);
104+
}
105+
return updateCall(node, expression, /*typeArguments*/ undefined, args);
106+
}
94107
return visitEachChild(node, visitor, context);
95108
}
96109

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)