Stage 0 Draft / January 30, 2016

Optional Chaining

1Scope#

This is the spec text proposal for introducing Optional Chaining feature (aka Null Propagation, aka Existential Operator) in ECMAScript.

For the syntax, we use the ?. token, with a lookahead at the level of the lexical grammar that allows to discriminate between a?.b (optional chaining) and a?.3:0 (conditional operator, whose meaning cannot be changed due to backward compatibility constraints).

Modified content are marked like this. In order to avoid distraction, we don’t mark mere editorial amendments.

2The Nil Reference#

2.1(6.2.3) The Reference Specification Type#

Note

The Reference type is used to explain the behaviour of such operators as delete, typeof, the assignment operators, the super keyword and other language features. For example, the left-hand operand of an assignment is expected to produce a reference.

A Reference is a resolved name or property binding. A Reference consists of one to four components, the base value, the referenced name, the Boolean valued strict reference flag, and the thisValue component.

The following abstract operations are used in this specification to access the components of references:

The following abstract operations are used in this specification to operate on references:

2.1.1(6.2.3.1) GetValue (V)#

  1. ReturnIfAbrupt(V).
  2. If Type(V) is not Reference, return V.
  3. Let base be GetBase(V).
  4. If IsNilReference(V) is true, return undefined.
  5. If IsUnresolvableReference(V), throw a ReferenceError exception.
  6. If IsPropertyReference(V), then
    1. If HasPrimitiveBase(V) is true, then
      1. Assert: In this case, base will never be null or undefined.
      2. Let base be ToObject(base).
    2. Return ? base.[[Get]](GetReferencedName(V), GetThisValue(V)).
  7. Else base must be an Environment Record,
    1. Return ? base.GetBindingValue(GetReferencedName(V), IsStrictReference(V)) (see 8.1.1).
Note

The object that may be created in step 5.a.ii is not accessible outside of the above abstract operation and the ordinary object [[Get]] internal method. An implementation might choose to avoid the actual creation of the object.

2.1.2(6.2.3.2) PutValue (V, W)#

  1. ReturnIfAbrupt(V).
  2. ReturnIfAbrupt(W).
  3. If Type(V) is not Reference, throw a ReferenceError exception.
  4. Let base be GetBase(V).
  5. If IsNilReference(V) is true, return.
  6. If IsUnresolvableReference(V), then
    1. If IsStrictReference(V) is true, then
      1. Throw a ReferenceError exception.
    2. Let globalObj be GetGlobalObject().
    3. Return ? Set(globalObj, GetReferencedName(V), W, false).
  7. Else if IsPropertyReference(V), then
    1. If HasPrimitiveBase(V) is true, then
      1. Assert: In this case, base will never be null or undefined.
      2. Set base to ToObject(base).
    2. Let succeeded be ? base.[[Set]](GetReferencedName(V), W, GetThisValue(V)).
    3. If succeeded is false and IsStrictReference(V) is true, throw a TypeError exception.
    4. Return.
  8. Else base must be an Environment Record.
    1. Return ? base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V)) (see 8.1.1).
Note

The object that may be created in step 6.a.ii is not accessible outside of the above algorithm and the ordinary object [[Set]] internal method. An implementation might choose to avoid the actual creation of that object.

2.2(12.5.4) The delete Operator#

2.2.1(12.5.4.2) Runtime Semantics: Evaluation#

UnaryExpression:deleteUnaryExpression
  1. Let ref be the result of evaluating UnaryExpression.
  2. ReturnIfAbrupt(ref).
  3. If Type(ref) is not Reference, return true.
  4. If IsNilReference(ref) is true, return true.
  5. If IsUnresolvableReference(ref) is true, then
    1. Assert: IsStrictReference(ref) is false.
    2. Return true.
  6. If IsPropertyReference(ref) is true, then
    1. If IsSuperReference(ref), throw a ReferenceError exception.
    2. Let baseObj be ! ToObject(GetBase(ref)).
    3. Let deleteStatus be ? baseObj.[[Delete]](GetReferencedName(ref)).
    4. If deleteStatus is false and IsStrictReference(ref) is true, throw a TypeError exception.
    5. Return deleteStatus.
  7. Else ref is a Reference to an Environment Record binding,
    1. Let bindings be GetBase(ref).
    2. Return ? bindings.DeleteBinding(GetReferencedName(ref)).
Note

When a delete operator occurs within strict mode code, a SyntaxError exception is thrown if its UnaryExpression is a direct reference to a variable, function argument, or function name. In addition, if a delete operator occurs within strict mode code and the property to be deleted has the attribute { [[Configurable]]: false }, a TypeError exception is thrown.

3(11.7) Lexical Grammar: Punctuators#

Syntax

OptionalChainingOperator::?.[lookahead ∉ DecimalDigit] OtherPunctuator::one of{()[]....;,<><=>===!====!==+-*%++--<<>>>>>&|^!~&&||?:=+=-=*=%=<<=>>=>>>=&=|=^==> Punctuator::OptionalChainingOperator OtherPunctuator DivPunctuator::/ /= RightBracePunctuator::}

4(12.3) Left-Hand-Side Expressions#

Syntax

MemberExpression[Yield]:PrimaryExpression[?Yield] MemberExpression[?Yield][Expression[In, ?Yield]] MemberExpression[?Yield]OptionalChainingOperator[Expression[In, ?Yield]] MemberExpression[?Yield].IdentifierName MemberExpression[?Yield]OptionalChainingOperatorIdentifierName MemberExpression[?Yield]TemplateLiteral[?Yield] SuperProperty[?Yield] MetaProperty newMemberExpression[?Yield]Arguments[?Yield] newMemberExpression[?Yield]OptionalChainingOperatorArguments[?Yield] SuperProperty[Yield]:super[Expression[In, ?Yield]] super.IdentifierName MetaProperty:NewTarget NewTarget:new.target NewExpression[Yield]:MemberExpression[?Yield] newNewExpression[?Yield] CallExpression[Yield]:MemberExpression[?Yield]Arguments[?Yield] SuperCall[?Yield] CallExpression[?Yield]Arguments[?Yield] CallExpression[?Yield]OptionalChainingOperatorArguments[?Yield] CallExpression[?Yield][Expression[In, ?Yield]] CallExpression[?Yield]OptionalChainingOperator[Expression[In, ?Yield]] CallExpression[?Yield].IdentifierName CallExpression[?Yield]OptionalChainingOperatorIdentifierName CallExpression[?Yield]TemplateLiteral[?Yield] SuperCall[Yield]:superArguments[?Yield] Arguments[Yield]:() (ArgumentList[?Yield]) ArgumentList[Yield]:AssignmentExpression[In, ?Yield] ...AssignmentExpression[In, ?Yield] ArgumentList[?Yield],AssignmentExpression[In, ?Yield] ArgumentList[?Yield],...AssignmentExpression[In, ?Yield] LeftHandSideExpression[Yield]:NewExpression[?Yield] CallExpression[?Yield]

4.1(12.3.1) Static Semantics#

4.1.1Static Semantics: Contains#

With parameter symbol.

MemberExpression:MemberExpression.IdentifierName
  1. If MemberExpression Contains symbol is true, return true.
  2. If symbol is a ReservedWord, return false.
  3. If symbol is an Identifier and StringValue of symbol is the same value as the StringValue of IdentifierName, return true.
  4. Return false.
MemberExpression:MemberExpressionOptionalChainingOperatorIdentifierName
  1. If MemberExpression Contains symbol is true, return true.
  2. If symbol is a ReservedWord, return false.
  3. If symbol is an Identifier and StringValue of symbol is the same value as the StringValue of IdentifierName, return true.
  4. Return false.
SuperProperty:super.IdentifierName
  1. If symbol is the ReservedWord super, return true.
  2. If symbol is a ReservedWord, return false.
  3. If symbol is an Identifier and StringValue of symbol is the same value as the StringValue of IdentifierName, return true.
  4. Return false.
CallExpression:CallExpression.IdentifierName
  1. If CallExpression Contains symbol is true, return true.
  2. If symbol is a ReservedWord, return false.
  3. If symbol is an Identifier and StringValue of symbol is the same value as the StringValue of IdentifierName, return true.
  4. Return false.
CallExpression:CallExpressionOptionalChainingOperatorIdentifierName
  1. If CallExpression Contains symbol is true, return true.
  2. If symbol is a ReservedWord, return false.
  3. If symbol is an Identifier and StringValue of symbol is the same value as the StringValue of IdentifierName, return true.
  4. Return false.

4.1.2Static Semantics: IsFunctionDefinition#

MemberExpression:MemberExpression[Expression] MemberExpressionOptionalChainingOperator[Expression] MemberExpression.IdentifierName MemberExpressionOptionalChainingOperatorIdentifierName MemberExpressionTemplateLiteral SuperProperty MetaProperty newMemberExpressionArguments newMemberExpressionOptionalChainingOperatorArguments NewExpression:newNewExpression CallExpression:MemberExpressionArguments SuperCall CallExpressionArguments CallExpressionOptionalChainingOperatorArguments CallExpression[Expression] CallExpressionOptionalChainingOperator[Expression] CallExpression.IdentifierName CallExpressionOptionalChainingOperatorIdentifierName CallExpressionTemplateLiteral
  1. Return false.

4.1.3Static Semantics: IsDestructuring#

MemberExpression:PrimaryExpression
  1. If PrimaryExpression is either an ObjectLiteral or an ArrayLiteral, return true.
  2. Return false.
MemberExpression:MemberExpression[Expression] MemberExpressionOptionalChainingOperator[Expression] MemberExpression.IdentifierName MemberExpressionOptionalChainingOperatorIdentifierName MemberExpressionTemplateLiteral SuperProperty MetaProperty newMemberExpressionArguments newMemberExpressionOptionalChainingOperatorArguments NewExpression:newNewExpression CallExpression:MemberExpressionArguments SuperCall CallExpressionArguments CallExpressionOptionalChainingOperatorArguments CallExpression[Expression] CallExpressionOptionalChainingOperator[Expression] CallExpression.IdentifierName CallExpressionOptionalChainingOperatorIdentifierName CallExpressionTemplateLiteral
  1. Return false.

4.1.4Static Semantics: IsIdentifierRef#

LeftHandSideExpression:CallExpression MemberExpression:MemberExpression[Expression] MemberExpressionOptionalChainingOperator[Expression] MemberExpression.IdentifierName MemberExpressionOptionalChainingOperatorIdentifierName MemberExpressionTemplateLiteral SuperProperty MetaProperty newMemberExpressionArguments newMemberExpressionOptionalChainingOperatorArguments NewExpression:newNewExpression
  1. Return false.

4.1.5Static Semantics: IsValidSimpleAssignmentTarget#

CallExpression:CallExpression[Expression] CallExpressionOptionalChainingOperator[Expression] CallExpression.IdentifierName CallExpressionOptionalChainingOperatorIdentifierName MemberExpression:MemberExpression[Expression] MemberExpressionOptionalChainingOperator[Expression] MemberExpression.IdentifierName MemberExpressionOptionalChainingOperatorIdentifierName SuperProperty
  1. Return true.
CallExpression:MemberExpressionArguments SuperCall CallExpressionArguments CallExpressionOptionalChainingOperatorArguments CallExpressionTemplateLiteral NewExpression:newNewExpression MemberExpression:MemberExpressionTemplateLiteral newMemberExpressionArguments newMemberExpressionOptionalChainingOperatorArguments NewTarget:new.target
  1. Return false.

4.2(12.3.2) Property Accessors#

Note

Properties are accessed by name, using either the dot notation:

or the bracket notation:

The dot notation is explained by the following syntactic conversion:

is identical in its behaviour to

MemberExpression [ <identifier-name-string> ]

and similarly

is identical in its behaviour to

CallExpression [ <identifier-name-string> ]

where <identifier-name-string> is the result of evaluating StringValue of IdentifierName.

4.2.1Runtime Semantics: Evaluation#

MemberExpression:MemberExpression[Expression]
  1. Let baseReference be the result of evaluating MemberExpression.
  2. ReturnIfAbrupt(baseReference).
  3. If baseReference is a Reference and IsNilReference(baseReference), then
    1. Return baseReference.
  4. Let baseValue be ? GetValue(baseReference).
  5. Let propertyNameReference be the result of evaluating Expression.
  6. Let propertyNameValue be ? GetValue(propertyNameReference).
  7. Let bv be ? RequireObjectCoercible(baseValue).
  8. Let propertyKey be ? ToPropertyKey(propertyNameValue).
  9. If the code matched by the syntactic production that is being evaluated is strict mode code, let strict be true, else let strict be false.
  10. Return a value of type Reference whose base value is bv and whose referenced name is propertyKey, and whose strict reference flag is strict.
MemberExpression:MemberExpressionOptionalChainingOperator[Expression]
  1. Let baseReference be the result of evaluating MemberExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. If baseValue is undefined or null, then
    1. Return a value of type Reference whose base value is Nil.
  4. Let propertyNameReference be the result of evaluating Expression.
  5. Let propertyNameValue be ? GetValue(propertyNameReference).
  6. Let bv be ? RequireObjectCoercible(baseValue).
  7. Let propertyKey be ? ToPropertyKey(propertyNameValue).
  8. If the code matched by the syntactic production that is being evaluated is strict mode code, let strict be true, else let strict be false.
  9. Return a value of type Reference whose base value is bv and whose referenced name is propertyKey, and whose strict reference flag is strict.
MemberExpression:MemberExpression.IdentifierName
  1. Let baseReference be the result of evaluating MemberExpression.
  2. ReturnIfAbrupt(baseReference).
  3. If baseReference is a Reference and IsNilReference(baseReference), then
    1. Return baseReference.
  4. Let baseValue be ? GetValue(baseReference).
  5. Let bv be ? RequireObjectCoercible(baseValue).
  6. Let propertyNameString be StringValue of IdentifierName.
  7. If the code matched by the syntactic production that is being evaluated is strict mode code, let strict be true, else let strict be false.
  8. Return a value of type Reference whose base value is bv and whose referenced name is propertyNameString, and whose strict reference flag is strict.
MemberExpression:MemberExpressionOptionalChainingOperatorIdentifierName
  1. Let baseReference be the result of evaluating MemberExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. If baseValue is undefined or null, then
    1. Return a value of type Reference whose base value is Nil.
  4. Let bv be ? RequireObjectCoercible(baseValue).
  5. Let propertyNameString be StringValue of IdentifierName.
  6. If the code matched by the syntactic production that is being evaluated is strict mode code, let strict be true, else let strict be false.
  7. Return a value of type Reference whose base value is bv and whose referenced name is propertyNameString, and whose strict reference flag is strict.
CallExpression:CallExpression[Expression]

Is evaluated in exactly the same manner as MemberExpression:MemberExpression[Expression] except that the contained CallExpression is evaluated in step 1.

CallExpression:CallExpressionOptionalChainingOperator[Expression]

Is evaluated in exactly the same manner as MemberExpression:MemberExpressionOptionalChainingOperator[Expression] except that the contained CallExpression is evaluated in step 1.

CallExpression:CallExpression.IdentifierName

Is evaluated in exactly the same manner as MemberExpression:MemberExpression.IdentifierName except that the contained CallExpression is evaluated in step 1.

CallExpression:CallExpressionOptionalChainingOperatorIdentifierName

Is evaluated in exactly the same manner as MemberExpression:MemberExpressionOptionalChainingOperatorIdentifierName except that the contained CallExpression is evaluated in step 1.

4.3(12.3.3) The new Operator#

4.3.1Runtime Semantics: Evaluation#

NewExpression:newNewExpression
  1. Return ? EvaluateNew(NewExpression, empty, false).
MemberExpression:newMemberExpressionArguments
  1. Return ? EvaluateNew(MemberExpression, Arguments, false).
MemberExpression:newMemberExpressionOptionalChainingOperatorArguments
  1. Return ? EvaluateNew(MemberExpression, Arguments, true).

4.3.1.1Runtime Semantics: EvaluateNew(constructProduction, arguments, optionalEvaluation)#

The abstract operation EvaluateNew with arguments constructProduction, arguments, and optionalEvaluation performs the following steps:

  1. Assert: constructProduction is either a NewExpression or a MemberExpression.
  2. Assert: arguments is either empty or an Arguments production.
  3. Let ref be the result of evaluating constructProduction.
  4. ReturnIfAbrupt(ref).
  5. If ref is a Reference and IsNilReference(ref), then
    1. Return ref.
  6. Let constructor be ? GetValue(ref).
  7. If optionalEvaluation is true and constructor is undefined or null, then
    1. Return a value of type Reference whose base value is Nil.
  8. If arguments is empty, let argList be an empty List.
  9. Else,
    1. Let argList be ArgumentListEvaluation of arguments.
    2. ReturnIfAbrupt(argList).
  10. If IsConstructor(constructor) is false, throw a TypeError exception.
  11. Return ? Construct(constructor, argList).

4.4(12.3.4) Function Calls#

4.4.1Runtime Semantics: Evaluation#

CallExpression:MemberExpressionArguments
  1. Let ref be the result of evaluating MemberExpression.
  2. Let func be ? GetValue(ref).
  3. If Type(ref) is Reference and IsPropertyReference(ref) is false and GetReferencedName(ref) is "eval", then
    1. If SameValue(func, %eval%) is true, then
      1. Let argList be ? ArgumentListEvaluation(Arguments).
      2. If argList has no elements, return undefined.
      3. Let evalText be the first element of argList.
      4. If the source code matching this CallExpression is strict code, let strictCaller be true. Otherwise let strictCaller be false.
      5. Let evalRealm be the current Realm Record.
      6. Return ? PerformEval(evalText, evalRealm, strictCaller, true).
  4. If Type(ref) is Reference, then
    1. If IsNilReference(ref), then
      1. Return ref.
    2. If IsPropertyReference(ref) is true, then
      1. Let thisValue be GetThisValue(ref).
    3. Else, the base of ref is an Environment Record
      1. Let refEnv be GetBase(ref).
      2. Let thisValue be refEnv.WithBaseObject().
  5. Else Type(ref) is not Reference,
    1. Let thisValue be undefined.
  6. Let thisCall be this CallExpression.
  7. Let tailCall be IsInTailPosition(thisCall). (See 14.6.1)
  8. Return ? EvaluateDirectCall(func, thisValue, Arguments, tailCall, false).

A CallExpression evaluation that executes step 3.a.vi is a direct eval.

CallExpression:MemberExpressionArguments

Is evaluated in the same manner as CallExpression:MemberExpressionOptionalChainingOperatorArguments except for the last step which is replaced by:

  1. Return ? EvaluateDirectCall(func, thisValue, Arguments, tailCall, true).
CallExpression:CallExpressionArguments
  1. Let ref be the result of evaluating CallExpression.
  2. Let thisCall be this CallExpression.
  3. Let tailCall be IsInTailPosition(thisCall). (See 14.6.1)
  4. Return ? EvaluateCall(ref, Arguments, tailCall, false).
CallExpression:CallExpressionOptionalChainingOperatorArguments
  1. Let ref be the result of evaluating CallExpression.
  2. Let thisCall be this CallExpression.
  3. Let tailCall be IsInTailPosition(thisCall). (See 14.6.1)
  4. Return ? EvaluateCall(ref, Arguments, tailCall, true).

4.4.2Runtime Semantics: EvaluateCall( ref, arguments, tailPosition, optionalEvaluation )#

The abstract operation EvaluateCall takes as arguments a value ref, a syntactic grammar production arguments, a Boolean argument tailPosition, and a Boolean argument optionalEvaluation. It performs the following steps:

  1. Let func be ? GetValue(ref).
  2. If Type(ref) is Reference, then
    1. If IsNilReference(ref), then
      1. Return ref.
    2. If IsPropertyReference(ref) is true, then
      1. Let thisValue be GetThisValue(ref).
    3. Else, the base of ref is an Environment Record
      1. Let refEnv be GetBase(ref).
      2. Let thisValue be refEnv.WithBaseObject().
  3. Else Type(ref) is not Reference,
    1. Let thisValue be undefined.
  4. Return ? EvaluateDirectCall(func, thisValue, arguments, tailPosition, optionalEvaluation).

4.4.3Runtime Semantics: EvaluateDirectCall( func, thisValue, arguments, tailPosition, optionalEvaluation )#

The abstract operation EvaluateDirectCall takes as arguments a value func, a value thisValue, a syntactic grammar production arguments, a Boolean argument tailPosition, and a Boolean argument optionalEvaluation. It performs the following steps:

  1. If optionalEvaluation is true and func is undefined or null, then
    1. Return a value of type Reference whose base value is Nil.
  2. Let argList be ? ArgumentListEvaluation(arguments).
  3. If Type(func) is not Object, throw a TypeError exception.
  4. If IsCallable(func) is false, throw a TypeError exception.
  5. If tailPosition is true, perform PrepareForTailCall().
  6. Let result be Call(func, thisValue, argList).
  7. Assert: If tailPosition is true, the above call will not return here, but instead evaluation will continue as if the following return has already occurred.
  8. Assert: If result is not an abrupt completion then Type(result) is an ECMAScript language type.
  9. Return result.

4.5(12.3.5) The super Keyword#

(not modified)

4.6(12.3.6) Argument Lists#

(not modified)

4.7(12.3.7) Tagged Templates#

Note

A tagged template is a function call where the arguments of the call are derived from a TemplateLiteral (12.2.9). The actual arguments include a template object (12.2.9.3) and the values produced by evaluating the expressions embedded within the TemplateLiteral.

4.7.1Runtime Semantics: Evaluation#

MemberExpression:MemberExpressionTemplateLiteral
  1. Let tagRef be the result of evaluating MemberExpression.
  2. Let thisCall be this MemberExpression.
  3. Let tailCall be IsInTailPosition(thisCall). (See 14.6.1)
  4. Return ? EvaluateCall(tagRef, TemplateLiteral, tailCall, false).
CallExpression:CallExpressionTemplateLiteral
  1. Let tagRef be the result of evaluating CallExpression.
  2. Let thisCall be this CallExpression.
  3. Let tailCall be IsInTailPosition(thisCall). (See 14.6.1)
  4. Return ? EvaluateCall(tagRef, TemplateLiteral, tailCall, false).

4.8(12.3.8) Meta Properties#

(not modified)