babel: Optional Chaining Bugs Checklist

  • Delete on optional chain should work delete a?.b: https://github.com/babel/babel/pull/7257
  • Optional should be removed from NewExpression AST
  • Optional MemberExpressions need to become a new AST node OptionalChain, to fix issues with should-not-parse grammars
  • Ensure OptionalChain cannot be the LHS of Assignment/Update expressions, tag of a TaggedTemplateLiteral, have a Super object, nor the callee of a NewExpresion

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 29 (29 by maintainers)

Commits related to this issue

Most upvoted comments

With tri-state optional field:

obj.a
{ // MemberExpression
  object: Identifier("obj"),
  property: Identifier("a"),
  computed: false,
  optional: undefined,
}

obj.a.b
{ // MemberExpression
  object: { // MemberExpression
    object: Identifier("obj"),
    property: Identifier("a"),
    computed: false,
    optional: undefined,
  },
  property: Identifier("b"),
  computed: false,
  optional: undefined,
}

obj?.a
{ // MemberExpression
  object: Identifier("obj"),
  property: Identifier("a"),
  computed: false,
  optional: true,
}

obj?.a.b
{ // MemberExpression
  object: { // MemberExpression
    object: Identifier("obj"),
    property: Identifier("a"),
    computed: false,
    optional: true,
  },
  property: Identifier("b"),
  computed: false,
  optional: false,
}

obj?.a?.b
{ // MemberExpression
  object: { // MemberExpression
    object: Identifier("obj"),
    property: Identifier("a"),
    computed: false,
    optional: true,
  },
  property: Identifier("b"),
  computed: false,
  optional: true,
}

(obj?.a).b
{ // MemberExpression
  object: { // MemberExpression
    object: Identifier("obj"),
    property: Identifier("a"),
    computed: false,
    optional: true,
  },
  property: Identifier("b"),
  computed: false,
  optional: undefined,
}

That is OptionalChain. 😉

Spec names don’t matter for us. What it’s trying to define is two separate member expressions:

obj?.a

Is an OptionalMemberExpression, with object Identifier("obj"), property Identifier("a"), computed false, and optional true.

If we then add a .b to it, we get another OptionalMemberExpression (this is the OptionalChain thing):

{ // OptionalMemberExpression
  object: { // OptionalMemberExpression
    object: Identifier("obj"),
    property: Identifier("a"),
    computed: false,
    optional: true,
  },
  property: Identifier("b"),
  computed: false,
  optional: false,
}

Notice that .b’s optional is false, even though it is still an OptionalMemberExpression. Now, the object can be either a OptionalMemberExpression, MemberExpression, or a CallExpression.

Also note that OptionalMemberExpression can turn into a regular MemberExpression if surrounded by parenthesis (due to syntax grammars):

(obj?.a).b

// ast:
{ // MemberExpression
  object: { // OptionalMemberExpression
    object: Identifier("obj"),
    property: Identifier("a"),
    computed: false,
    optional: true,
  },
  property: Identifier("b"),
  computed: false,
}

We can name it whatever. Leaving it OptionalMemberExpression will likely be easiest for transforms.

OptionalChainingPunctuator is just the ??. sigil.

Hi @nveenjain!

Part 1 is already done.

Part 2/3 are closely intertwined. Unfortunately, they’re rather difficult to accomplish. It’ll take creating a brand new node type, called OptionalMemberExpression, which propogates upwards, and can be broken by a non-optional member or call expression (or a parenthesis, because syntax). It’ll also likely break things inside the babel transforms, since they now have to handle a new member syntax.