Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions 38 src/compiler/transformers/esnext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace ts {
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.CallExpression:
if (node.flags & NodeFlags.OptionalChain) {
const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false);
const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false, /*isDelete*/ false);
Debug.assertNotNode(updated, isSyntheticReference);
return updated;
}
Expand All @@ -34,6 +34,8 @@ namespace ts {
return transformNullishCoalescingExpression(<BinaryExpression>node);
}
return visitEachChild(node, visitor, context);
case SyntaxKind.DeleteExpression:
return visitDeleteExpression(node as DeleteExpression);
default:
return visitEachChild(node, visitor, context);
}
Expand All @@ -48,8 +50,8 @@ namespace ts {
return { expression: chain.expression, chain: links };
}

function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean): Expression {
const expression = visitNonOptionalExpression(node.expression, captureThisArg);
function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean, isDelete: boolean): Expression {
const expression = visitNonOptionalExpression(node.expression, captureThisArg, isDelete);
if (isSyntheticReference(expression)) {
// `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` }
// `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` }
Expand All @@ -58,10 +60,10 @@ namespace ts {
return updateParen(node, expression);
}

function visitNonOptionalPropertyOrElementAccessExpression(node: AccessExpression, captureThisArg: boolean): Expression {
function visitNonOptionalPropertyOrElementAccessExpression(node: AccessExpression, captureThisArg: boolean, isDelete: boolean): Expression {
if (isOptionalChain(node)) {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return visitOptionalExpression(node, captureThisArg);
return visitOptionalExpression(node, captureThisArg, isDelete);
}

let expression: Expression = visitNode(node.expression, visitor, isExpression);
Expand All @@ -87,24 +89,24 @@ namespace ts {
function visitNonOptionalCallExpression(node: CallExpression, captureThisArg: boolean): Expression {
if (isOptionalChain(node)) {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return visitOptionalExpression(node, captureThisArg);
return visitOptionalExpression(node, captureThisArg, /*isDelete*/ false);
}
return visitEachChild(node, visitor, context);
}

function visitNonOptionalExpression(node: Expression, captureThisArg: boolean): Expression {
function visitNonOptionalExpression(node: Expression, captureThisArg: boolean, isDelete: boolean): Expression {
switch (node.kind) {
case SyntaxKind.ParenthesizedExpression: return visitNonOptionalParenthesizedExpression(node as ParenthesizedExpression, captureThisArg);
case SyntaxKind.ParenthesizedExpression: return visitNonOptionalParenthesizedExpression(node as ParenthesizedExpression, captureThisArg, isDelete);
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression: return visitNonOptionalPropertyOrElementAccessExpression(node as AccessExpression, captureThisArg);
case SyntaxKind.ElementAccessExpression: return visitNonOptionalPropertyOrElementAccessExpression(node as AccessExpression, captureThisArg, isDelete);
case SyntaxKind.CallExpression: return visitNonOptionalCallExpression(node as CallExpression, captureThisArg);
default: return visitNode(node, visitor, isExpression);
}
}

function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean): Expression {
function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean, isDelete: boolean): Expression {
const { expression, chain } = flattenChain(node);
const left = visitNonOptionalExpression(expression, isCallChain(chain[0]));
const left = visitNonOptionalExpression(expression, isCallChain(chain[0]), /*isDelete*/ false);
const leftThisArg = isSyntheticReference(left) ? left.thisArg : undefined;
let leftExpression = isSyntheticReference(left) ? left.expression : left;
let capturedLeft: Expression = leftExpression;
Expand Down Expand Up @@ -152,11 +154,9 @@ namespace ts {
setOriginalNode(rightExpression, segment);
}

const target = createConditional(
createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true),
createVoidZero(),
rightExpression,
);
const target = isDelete
? createConditional(createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true), createTrue(), createDelete(rightExpression))
: createConditional(createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true), createVoidZero(), rightExpression);
return thisArg ? createSyntheticReferenceExpression(target, thisArg) : target;
}

Expand Down Expand Up @@ -197,5 +197,11 @@ namespace ts {
expression.kind !== SyntaxKind.ThisKeyword &&
expression.kind !== SyntaxKind.SuperKeyword;
}

function visitDeleteExpression(node: DeleteExpression) {
return isOptionalChain(skipParentheses(node.expression))
? setOriginalNode(visitNonOptionalExpression(node.expression, /*captureThisArg*/ false, /*isDelete*/ true), node)
: updateDelete(node, visitNode(node.expression, visitor, isExpression));
}
}
}
42 changes: 42 additions & 0 deletions 42 tests/baselines/reference/deleteChain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//// [deleteChain.ts]
declare const o1: undefined | { b: string };
delete o1?.b;
delete (o1?.b);

declare const o2: undefined | { b: { c: string } };
delete o2?.b.c;
delete (o2?.b.c);

declare const o3: { b: undefined | { c: string } };
delete o3.b?.c;
delete (o3.b?.c);

declare const o4: { b?: { c: { d?: { e: string } } } };
delete o4.b?.c.d?.e;
delete (o4.b?.c.d)?.e;
delete (o4.b?.c.d?.e);

declare const o5: { b?(): { c: { d?: { e: string } } } };
delete o5.b?.().c.d?.e;
delete (o5.b?.().c.d?.e);

declare const o6: { b?: { c: { d?: { e: string } } } };
delete o6.b?.['c'].d?.['e'];
delete (o6.b?.['c'].d?.['e']);

//// [deleteChain.js]
"use strict";
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
o1 === null || o1 === void 0 ? true : delete o1.b;
(o1 === null || o1 === void 0 ? true : delete o1.b);
o2 === null || o2 === void 0 ? true : delete o2.b.c;
(o2 === null || o2 === void 0 ? true : delete o2.b.c);
(_a = o3.b) === null || _a === void 0 ? true : delete _a.c;
((_b = o3.b) === null || _b === void 0 ? true : delete _b.c);
(_d = (_c = o4.b) === null || _c === void 0 ? void 0 : _c.c.d) === null || _d === void 0 ? true : delete _d.e;
(_f = ((_e = o4.b) === null || _e === void 0 ? void 0 : _e.c.d)) === null || _f === void 0 ? true : delete _f.e;
((_h = (_g = o4.b) === null || _g === void 0 ? void 0 : _g.c.d) === null || _h === void 0 ? true : delete _h.e);
(_k = (_j = o5.b) === null || _j === void 0 ? void 0 : _j.call(o5).c.d) === null || _k === void 0 ? true : delete _k.e;
((_m = (_l = o5.b) === null || _l === void 0 ? void 0 : _l.call(o5).c.d) === null || _m === void 0 ? true : delete _m.e);
(_p = (_o = o6.b) === null || _o === void 0 ? void 0 : _o['c'].d) === null || _p === void 0 ? true : delete _p['e'];
((_r = (_q = o6.b) === null || _q === void 0 ? void 0 : _q['c'].d) === null || _r === void 0 ? true : delete _r['e']);
143 changes: 143 additions & 0 deletions 143 tests/baselines/reference/deleteChain.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
=== tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts ===
declare const o1: undefined | { b: string };
>o1 : Symbol(o1, Decl(deleteChain.ts, 0, 13))
>b : Symbol(b, Decl(deleteChain.ts, 0, 31))

delete o1?.b;
>o1?.b : Symbol(b, Decl(deleteChain.ts, 0, 31))
>o1 : Symbol(o1, Decl(deleteChain.ts, 0, 13))
>b : Symbol(b, Decl(deleteChain.ts, 0, 31))

delete (o1?.b);
>o1?.b : Symbol(b, Decl(deleteChain.ts, 0, 31))
>o1 : Symbol(o1, Decl(deleteChain.ts, 0, 13))
>b : Symbol(b, Decl(deleteChain.ts, 0, 31))

declare const o2: undefined | { b: { c: string } };
>o2 : Symbol(o2, Decl(deleteChain.ts, 4, 13))
>b : Symbol(b, Decl(deleteChain.ts, 4, 31))
>c : Symbol(c, Decl(deleteChain.ts, 4, 36))

delete o2?.b.c;
>o2?.b.c : Symbol(c, Decl(deleteChain.ts, 4, 36))
>o2?.b : Symbol(b, Decl(deleteChain.ts, 4, 31))
>o2 : Symbol(o2, Decl(deleteChain.ts, 4, 13))
>b : Symbol(b, Decl(deleteChain.ts, 4, 31))
>c : Symbol(c, Decl(deleteChain.ts, 4, 36))

delete (o2?.b.c);
>o2?.b.c : Symbol(c, Decl(deleteChain.ts, 4, 36))
>o2?.b : Symbol(b, Decl(deleteChain.ts, 4, 31))
>o2 : Symbol(o2, Decl(deleteChain.ts, 4, 13))
>b : Symbol(b, Decl(deleteChain.ts, 4, 31))
>c : Symbol(c, Decl(deleteChain.ts, 4, 36))

declare const o3: { b: undefined | { c: string } };
>o3 : Symbol(o3, Decl(deleteChain.ts, 8, 13))
>b : Symbol(b, Decl(deleteChain.ts, 8, 19))
>c : Symbol(c, Decl(deleteChain.ts, 8, 36))

delete o3.b?.c;
>o3.b?.c : Symbol(c, Decl(deleteChain.ts, 8, 36))
>o3.b : Symbol(b, Decl(deleteChain.ts, 8, 19))
>o3 : Symbol(o3, Decl(deleteChain.ts, 8, 13))
>b : Symbol(b, Decl(deleteChain.ts, 8, 19))
>c : Symbol(c, Decl(deleteChain.ts, 8, 36))

delete (o3.b?.c);
>o3.b?.c : Symbol(c, Decl(deleteChain.ts, 8, 36))
>o3.b : Symbol(b, Decl(deleteChain.ts, 8, 19))
>o3 : Symbol(o3, Decl(deleteChain.ts, 8, 13))
>b : Symbol(b, Decl(deleteChain.ts, 8, 19))
>c : Symbol(c, Decl(deleteChain.ts, 8, 36))

declare const o4: { b?: { c: { d?: { e: string } } } };
>o4 : Symbol(o4, Decl(deleteChain.ts, 12, 13))
>b : Symbol(b, Decl(deleteChain.ts, 12, 19))
>c : Symbol(c, Decl(deleteChain.ts, 12, 25))
>d : Symbol(d, Decl(deleteChain.ts, 12, 30))
>e : Symbol(e, Decl(deleteChain.ts, 12, 36))

delete o4.b?.c.d?.e;
>o4.b?.c.d?.e : Symbol(e, Decl(deleteChain.ts, 12, 36))
>o4.b?.c.d : Symbol(d, Decl(deleteChain.ts, 12, 30))
>o4.b?.c : Symbol(c, Decl(deleteChain.ts, 12, 25))
>o4.b : Symbol(b, Decl(deleteChain.ts, 12, 19))
>o4 : Symbol(o4, Decl(deleteChain.ts, 12, 13))
>b : Symbol(b, Decl(deleteChain.ts, 12, 19))
>c : Symbol(c, Decl(deleteChain.ts, 12, 25))
>d : Symbol(d, Decl(deleteChain.ts, 12, 30))
>e : Symbol(e, Decl(deleteChain.ts, 12, 36))

delete (o4.b?.c.d)?.e;
>(o4.b?.c.d)?.e : Symbol(e, Decl(deleteChain.ts, 12, 36))
>o4.b?.c.d : Symbol(d, Decl(deleteChain.ts, 12, 30))
>o4.b?.c : Symbol(c, Decl(deleteChain.ts, 12, 25))
>o4.b : Symbol(b, Decl(deleteChain.ts, 12, 19))
>o4 : Symbol(o4, Decl(deleteChain.ts, 12, 13))
>b : Symbol(b, Decl(deleteChain.ts, 12, 19))
>c : Symbol(c, Decl(deleteChain.ts, 12, 25))
>d : Symbol(d, Decl(deleteChain.ts, 12, 30))
>e : Symbol(e, Decl(deleteChain.ts, 12, 36))

delete (o4.b?.c.d?.e);
>o4.b?.c.d?.e : Symbol(e, Decl(deleteChain.ts, 12, 36))
>o4.b?.c.d : Symbol(d, Decl(deleteChain.ts, 12, 30))
>o4.b?.c : Symbol(c, Decl(deleteChain.ts, 12, 25))
>o4.b : Symbol(b, Decl(deleteChain.ts, 12, 19))
>o4 : Symbol(o4, Decl(deleteChain.ts, 12, 13))
>b : Symbol(b, Decl(deleteChain.ts, 12, 19))
>c : Symbol(c, Decl(deleteChain.ts, 12, 25))
>d : Symbol(d, Decl(deleteChain.ts, 12, 30))
>e : Symbol(e, Decl(deleteChain.ts, 12, 36))

declare const o5: { b?(): { c: { d?: { e: string } } } };
>o5 : Symbol(o5, Decl(deleteChain.ts, 17, 13))
>b : Symbol(b, Decl(deleteChain.ts, 17, 19))
>c : Symbol(c, Decl(deleteChain.ts, 17, 27))
>d : Symbol(d, Decl(deleteChain.ts, 17, 32))
>e : Symbol(e, Decl(deleteChain.ts, 17, 38))

delete o5.b?.().c.d?.e;
>o5.b?.().c.d?.e : Symbol(e, Decl(deleteChain.ts, 17, 38))
>o5.b?.().c.d : Symbol(d, Decl(deleteChain.ts, 17, 32))
>o5.b?.().c : Symbol(c, Decl(deleteChain.ts, 17, 27))
>o5.b : Symbol(b, Decl(deleteChain.ts, 17, 19))
>o5 : Symbol(o5, Decl(deleteChain.ts, 17, 13))
>b : Symbol(b, Decl(deleteChain.ts, 17, 19))
>c : Symbol(c, Decl(deleteChain.ts, 17, 27))
>d : Symbol(d, Decl(deleteChain.ts, 17, 32))
>e : Symbol(e, Decl(deleteChain.ts, 17, 38))

delete (o5.b?.().c.d?.e);
>o5.b?.().c.d?.e : Symbol(e, Decl(deleteChain.ts, 17, 38))
>o5.b?.().c.d : Symbol(d, Decl(deleteChain.ts, 17, 32))
>o5.b?.().c : Symbol(c, Decl(deleteChain.ts, 17, 27))
>o5.b : Symbol(b, Decl(deleteChain.ts, 17, 19))
>o5 : Symbol(o5, Decl(deleteChain.ts, 17, 13))
>b : Symbol(b, Decl(deleteChain.ts, 17, 19))
>c : Symbol(c, Decl(deleteChain.ts, 17, 27))
>d : Symbol(d, Decl(deleteChain.ts, 17, 32))
>e : Symbol(e, Decl(deleteChain.ts, 17, 38))

declare const o6: { b?: { c: { d?: { e: string } } } };
>o6 : Symbol(o6, Decl(deleteChain.ts, 21, 13))
>b : Symbol(b, Decl(deleteChain.ts, 21, 19))
>c : Symbol(c, Decl(deleteChain.ts, 21, 25))
>d : Symbol(d, Decl(deleteChain.ts, 21, 30))
>e : Symbol(e, Decl(deleteChain.ts, 21, 36))

delete o6.b?.['c'].d?.['e'];
>o6.b?.['c'].d : Symbol(d, Decl(deleteChain.ts, 21, 30))
>o6.b : Symbol(b, Decl(deleteChain.ts, 21, 19))
>o6 : Symbol(o6, Decl(deleteChain.ts, 21, 13))
>b : Symbol(b, Decl(deleteChain.ts, 21, 19))
>d : Symbol(d, Decl(deleteChain.ts, 21, 30))

delete (o6.b?.['c'].d?.['e']);
>o6.b?.['c'].d : Symbol(d, Decl(deleteChain.ts, 21, 30))
>o6.b : Symbol(b, Decl(deleteChain.ts, 21, 19))
>o6 : Symbol(o6, Decl(deleteChain.ts, 21, 13))
>b : Symbol(b, Decl(deleteChain.ts, 21, 19))
>d : Symbol(d, Decl(deleteChain.ts, 21, 30))

Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.