Typescript: Nullish Coalescing and Logical Compound Assignments (`??=`, `||=`, `&&=`)

Created on 6 Mar 2020  ·  9Comments  ·  Source: microsoft/TypeScript

Search Terms

Nullish coalescing assignment

Suggestion

A new operator ??= to assign some default to a variable/property if it is nullish.

It's also avaibale in PHP: https://wiki.php.net/rfc/null_coalesce_equal_operator

Examples

obj1.obj2.obj3.x ??= 42;
instead of
obj1.obj2.obj3.x = obj1.obj2.obj3.x ?? 42;

Checklist

My suggestion meets these guidelines:

  • [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [x] This feature would agree with the rest of TypeScript's Design Goals.
Committed ES Next Moderate Suggestion help wanted

Most helpful comment

It just hit stage 3!

All 9 comments

This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)

Most likely this one should be unchecked, because this syntax is not supported by ES.

However there is proposal https://github.com/tc39/proposal-logical-assignment which is in Stage 2 and TS usually start implementation if proposal reaches Stage 3.
@DanielRosenwasser might provide more info about Stage 3 of that proposal :)

Hm negation confusion :) I checked this item because "This isn't a runtime feature" as in "This is a compile-time feature" because TSC needs to compile it to something else. Or what else is intended behind this item?

Thanks for the TC pointer. Makes sense to extend this to all logical operators!

It's in a good direction in the standards pipeline. Iif I recall correctly, I was the only one who had some minor objections around https://github.com/tc39/proposal-logical-assignment/issues/3 which I've changed my mind on. We'll look into implementing it when it hits stage 3.

Could I take a try on this one after stage3?🙋🏻‍♂️

It looks like it'll be discussed tomorrow. I've given my review approval and I can let you know when it moves forward. I've put together a write-up to consider for any implementations:


This new proposal adds a few new productions:

LeftHandSideExpression &&= AssignmentExpression

LeftHandSideExpression ||= AssignmentExpression

LeftHandSideExpression ??= AssignmentExpression

These roughly correspond to the following respectively:

LeftHandSideExpression && (LeftHandSideExpression = AssignmentExpression)

LeftHandSideExpression || (LeftHandSideExpression = AssignmentExpression)

LeftHandSideExpression ?? (LeftHandSideExpression = AssignmentExpression)

An implementation for TypeScript should at least test the following:

  • JavaScript emit where the LeftHandSideExpression is

    • an Identifier

    • a property access or element access where



      • the target is an Identifier (e.g. a.b, a["b"])


      • the target is not an Identifier (e.g. a.b.c, a.b["c"], foo().c) - to ensure that side effects don't get retriggered



    • a parenthesized LeftHandSideExpression

    • at least one example where AssignmentTargetType of the LeftHandSideExpression in the spec is not simple.

  • Control flow analysis within

    • the right side of each operator



      • (see &&= example below)



    • the code following each operator



      • ts function foo(result: number[] | undefined) { results ||= []; results.push(100); }


      • ts function foo(result: number[] | undefined) { results ??= []; results.push(100); }


      • ts interface ThingWithOriginal { name: string; original?: ThingWithOriginal } function doSomethingWithAlias(thing?: ThingWithOriginal | undefined) { if (thing &&= thing.original) { console.log(thing.name); } }



  • the evaluated type of each operator

    • ts function foo(result: number[] | undefined) { (results ||= []).push(100); }

    • ts function foo(result: number[] | undefined) { (results ??= []).push(100); }

It just hit stage 3!

Yes, I saw your approve at tc39😳

Is this a bug or am I missing something?

let x: { a?: boolean } = {};

x.a ??= true;
x.a &&= false;
^^^ Type 'false' is not assignable to type 'true'.(2322)

Playground (Nightly build)

@aminpaks seems a bug. I'll take a look on it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

blendsdk picture blendsdk  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

DanielRosenwasser picture DanielRosenwasser  ·  3Comments

remojansen picture remojansen  ·  3Comments