Extend from the ChainingExpression approach.
extend interface Chain <: Node {
eventual: boolean
}Examples: (`optional` is always `false` in the following chain elements)
// obj~.aaa~.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj~.aaa.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "MemberChain",
"eventual": false,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj.aaa~.bbb
{
"type": "ChainingExpression",
"base": {
// For backward compatibility, non-eventual chains in the left of the first
// eventual chain are represented by the existing nodes.
"type": "MemberExpression",
"object": { "type": "Identifier", "name": "obj" },
"property": { "type": "Identifier", "name": "aaa" },
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj.aaa.bbb
{
// For backward compatibility, non-eventual chains in the left of the first
// eventual chain are represented by the existing nodes.
"type": "MemberExpression",
"object": {
"type": "MemberExpression",
"object": { "type": "Identifier", "name": "obj" },
"property": { "type": "Identifier", "name": "aaa" },
},
"property": { "type": "Identifier", "name": "bbb" },
}// (obj~.aaa)~.bbb
{
// Parentheses disconnect chains. Therefore, short-circuit behavior doesn't
// propagate.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (obj~.aaa).bbb
{
// Parentheses disconnect chains.
"type": "MemberExpression",
"object": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"property": { "type": "Identifier", "name": "bbb" },
}// func~.()~.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"arguments": [],
},
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// func~.().bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"arguments": [],
},
{
"type": "MemberChain",
"eventual": false,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (func~.())~.bbb
{
// Parentheses disconnect chains.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"arguments": [],
}
],
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (func~.()).bbb
{
// Parentheses disconnect chains.
"type": "MemberExpression",
"object": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"arguments": [],
}
],
},
"property": { "type": "Identifier", "name": "bbb" },
}// obj~.aaa~.()
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "CallChain",
"eventual": true,
"arguments": [],
}
],
}// obj~.aaa()
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "CallChain",
"eventual": false,
"arguments": [],
}
],
}// (obj~.aaa)~.()
{
// Parentheses disconnect chains.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"chain": [
{
"type": "CallChain",
"eventual": true,
"arguments": [],
}
],
}// (obj~.aaa)()
{
// Parentheses disconnect chains.
"type": "CallExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"arguments": [],
}Examples of integration with optional chaining
// obj?~.aaa?~.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj?~.aaa~.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj~.aaa?~.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj?~.aaa?.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "MemberChain",
"eventual": false,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj?~.aaa.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "MemberChain",
"eventual": false,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj~.aaa?.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "MemberChain",
"eventual": false,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// obj.aaa?~.bbb
{
"type": "ChainingExpression",
"base": {
// For backward compatibility, non-eventual chains in the left of the first
// eventual chain are represented by the existing nodes.
"type": "MemberExpression",
"object": { "type": "Identifier", "name": "obj" },
"property": { "type": "Identifier", "name": "aaa" },
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (obj?~.aaa)?~.bbb
{
// Parentheses disconnect chains. Therefore, short-circuit behavior doesn't
// propagate.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (obj?~.aaa)~.bbb
{
// Parentheses disconnect chains. Therefore, short-circuit behavior doesn't
// propagate.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (obj~.aaa)?~.bbb
{
// Parentheses disconnect chains. Therefore, short-circuit behavior doesn't
// propagate.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (obj?~.aaa).bbb
{
// Parentheses disconnect chains.
"type": "MemberExpression",
"object": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"property": { "type": "Identifier", "name": "bbb" },
}// func?~.()?~.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
},
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// func?~.()~.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
},
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// func~.()?~.bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": false,
"arguments": [],
},
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// func?~.().bbb
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
},
{
"type": "MemberChain",
"eventual": false,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (func?~.())?~.bbb
{
// Parentheses disconnect chains.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
}
],
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (func?~.())~.bbb
{
// Parentheses disconnect chains.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
}
],
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (func~.())?~.bbb
{
// Parentheses disconnect chains.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": false,
"arguments": [],
}
],
},
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "bbb" },
}
],
}// (func?~.()).bbb
{
// Parentheses disconnect chains.
"type": "MemberExpression",
"object": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "func" },
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
}
],
},
"property": { "type": "Identifier", "name": "bbb" },
}// obj?~.aaa?~.()
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
}
],
}// obj?~.aaa~.()
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "CallChain",
"eventual": true,
"optional": false,
"arguments": [],
}
],
}// obj~.aaa?~.()
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
}
],
}// obj?~.aaa()
{
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
},
{
"type": "CallChain",
"eventual": false,
"arguments": [],
}
],
}// (obj?~.aaa)?~.()
{
// Parentheses disconnect chains.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
}
],
}// (obj?~.aaa)~.()
{
// Parentheses disconnect chains.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": false,
"arguments": [],
}
],
}// (obj~.aaa)?~.()
{
// Parentheses disconnect chains.
"type": "ChainingExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": false,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"chain": [
{
"type": "CallChain",
"eventual": true,
"optional": true,
"arguments": [],
}
],
}// (obj?~.aaa)()
{
// Parentheses disconnect chains.
"type": "CallExpression",
"base": {
"type": "ChainingExpression",
"base": { "type": "Identifier", "name": "obj" },
"chain": [
{
"type": "MemberChain",
"eventual": true,
"optional": true,
"computed": false,
"property": { "type": "Identifier", "name": "aaa" },
}
],
},
"arguments": [],
}
Thanks for sketching this out. Looking forward to the decision on how Babel supports this syntax.