GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Nullish Coalescing and Logical Compound Assignments (`??=`, `||=`, `&&=`) · Issue #37255 · microsoft/TypeScript

Nullish Coalescing and Logical Compound Assignments (??=, ||=, &&=) #37255

New issue
Copy link
New issue
Copy link
Closed
#37727
Closed
Nullish Coalescing and Logical Compound Assignments (??=, ||=, &&=)#37255
#37727
Copy link
Labels
CommittedThe team has roadmapped this issueThe team has roadmapped this issueES NextNew featurers for ECMAScript (a.k.a. ESNext)New featurers for ECMAScript (a.k.a. ESNext)Effort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".SuggestionAn idea for TypeScriptAn idea for TypeScript
Milestone
TypeScript 4.0.0

Description

@danielrentz
Daniel Rentz (danielrentz)
opened on Mar 6, 2020
Issue body actions

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:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
Reacted by ExE Boss, Daniel Rosenwasser, Leonardo Dino, Lucas Willems, Kristian Gerardsson, Scott Talbot, Antoine ESTEVE, NN, Mark Salabutin, hfsaito and 23 more

Activity

  1. IllusionMH commented on Mar 6, 2020

    @IllusionMH
    Andrii Dieiev (IllusionMH)
    on Mar 6, 2020
    Contributor
    More actions

    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.
    Daniel Rosenwasser (@DanielRosenwasser) might provide more info about Stage 3 of that proposal :)

    Reacted by Steven and Gaurang Patel
  2. danielrentz commented on Mar 6, 2020

    @danielrentz
    Daniel Rentz (danielrentz)
    on Mar 6, 2020
    Author
    More actions

    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!

  3. Daniel Rosenwasser (DanielRosenwasser)
    added
    SuggestionAn idea for TypeScriptAn idea for TypeScript
    Waiting for TC39Unactionable until TC39 reaches some conclusionUnactionable until TC39 reaches some conclusion
    ES NextNew featurers for ECMAScript (a.k.a. ESNext)New featurers for ECMAScript (a.k.a. ESNext)
    on Mar 6, 2020
  4. DanielRosenwasser commented on Mar 6, 2020

    @DanielRosenwasser
    Daniel Rosenwasser (DanielRosenwasser)
    on Mar 6, 2020
    Member
    More actions

    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 tc39/proposal-logical-assignment#3 which I've changed my mind on. We'll look into implementing it when it hits stage 3.

    Reacted by Andrii Dieiev, ExE Boss, Craig Kovatch and Alfonso Andrés López Molina
  5. Daniel Rosenwasser (DanielRosenwasser)
    changed the title [-]Nullish coalescing assignment (??=)[/-] [+]Nullish Coalescing and Logical Compound Assignments (??=, ||=, &&=)[/+] on Mar 6, 2020
  6. Daniel Rosenwasser (DanielRosenwasser)
    changed the title [-]Nullish Coalescing and Logical Compound Assignments (??=, ||=, &&=)[/-] [+]Nullish Coalescing and Logical Compound Assignments (`??=`, `||=`, `&&=`)[/+] on Mar 6, 2020
  7. Kingwl commented on Mar 31, 2020

    @Kingwl
    Wenlu Wang (Kingwl)
    on Mar 31, 2020
    Contributor
    More actions

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

    Reacted by Andrii Dieiev, Jessica Franco, Danilo Fuchs, Cosimo Chellini, acrazing, Steven and Ali Torki
    Reacted by Cosimo Chellini
    Reacted by Daniel Rosenwasser, Jessica Franco, Kirill Groshkov, Liqueur Librazy and Cosimo Chellini
  8. DanielRosenwasser commented on Mar 31, 2020

    @DanielRosenwasser
    Daniel Rosenwasser (DanielRosenwasser)
    on Mar 31, 2020
    Member
    More actions

    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
        • function foo(result: number[] | undefined) {
              results ||= [];
              results.push(100);
          }
        • function foo(result: number[] | undefined) {
              results ??= [];
              results.push(100);
          }
        • 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
      • function foo(result: number[] | undefined) {
            (results ||= []).push(100);
        }
      • function foo(result: number[] | undefined) {
            (results ??= []).push(100);
        }
    Reacted by Leonardo Dino, Roger Padilla, Newerton, fs and Ali Torki
    Reacted by Jessica Franco and Roger Padilla
  9. DanielRosenwasser commented on Mar 31, 2020

    @DanielRosenwasser
    Daniel Rosenwasser (DanielRosenwasser)
    on Mar 31, 2020
    Member
    More actions

    It just hit stage 3!

    Reacted by Andrii Dieiev, Jessica Franco, Kenta Moriuchi, 程方, ExE Boss, Ryan Cavanaugh, Paul Dilley, Leonardo Dino, Dan Vanderkam, Peter Fernandes and 18 more
    Reacted by Ali Torki
    Reacted by Leonardo Dino, Kirill Groshkov, Carlos Azocar, Roger Padilla, Eric Lambrecht, Artyom Stepanishchev and Ali Torki
  10. Daniel Rosenwasser (DanielRosenwasser)
    added
    CommittedThe team has roadmapped this issueThe team has roadmapped this issue
    and removed
    Waiting for TC39Unactionable until TC39 reaches some conclusionUnactionable until TC39 reaches some conclusion
    on Mar 31, 2020
  11. Daniel Rosenwasser (DanielRosenwasser)
    added this to the TypeScript 4.0 milestone on Mar 31, 2020
  12. Daniel Rosenwasser (DanielRosenwasser)
    added
    help wanted
    Effort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".
    on Mar 31, 2020
  13. Kingwl commented on Apr 1, 2020

    @Kingwl
    Wenlu Wang (Kingwl)
    on Apr 1, 2020
    Contributor
    More actions

    Yes, I saw your approve at tc39😳

  14. Daniel Rosenwasser (DanielRosenwasser)
    closed this as completedin #37727on Jun 9, 2020
  15. aminpaks commented on Jun 16, 2020

    @aminpaks
    Amin Pakseresht (aminpaks)
    on Jun 16, 2020
    Contributor
    More actions

    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)

    Reacted by Benjamin Lupton and Aleksey Levenstein
  16. Kingwl commented on Jun 16, 2020

    @Kingwl
    Wenlu Wang (Kingwl)
    on Jun 16, 2020
    Contributor
    More actions

    Amin Pakseresht (@aminpaks) seems a bug. I'll take a look on it.

  17. kolpav commented on Feb 16, 2021

    @kolpav
    Pavel Kolář (kolpav)
    on Feb 16, 2021
    More actions

    Wenlu Wang (@Kingwl) is this bug too?

    Playground

    I don't see how props.size can be undefined

    Reacted by Gustavo and Gabriel
  18. danielrentz commented on Feb 16, 2021

    @danielrentz
    Daniel Rentz (danielrentz)
    on Feb 16, 2021
    Author
    More actions

    Pavel Kolář (@kolpav) I think this is unrelated but caused by the fact that narrowed types do not flow into callback functions.

    You can workaround that by creating a local const size = prop.size ?? 'base'; and use that.

    Reacted by Gustavo
  19. kolpav commented on Feb 16, 2021

    @kolpav
    Pavel Kolář (kolpav)
    on Feb 16, 2021
    More actions

    Daniel Rentz (@danielrentz) Thats unfortunate. I would like to keep react props on props object because using your solution I need to have two ways how to access props based on them having default value or not. props.<propWithoutDefaultValue> and special variables (size etc) for every other prop. I know there are ways how to get around that but they are usually lot of code and not as readable and nice as props.size ??= 'base'. Is it something worth creating issue for or is it expected TS behaviour? My mental model must be wrong because I was bit surprised by it.

    Reacted by Gustavo
  20. gustavopch commented on Jun 24, 2021

    @gustavopch
    Gustavo (gustavopch)
    on Jun 24, 2021
    More actions

    Pavel Kolář (@kolpav) I'm in the same situation. I don't see how props.size could be undefined. A workaround is to assign the default value using a function with an assertion signature:

    type NonNullableProp<
      TObject extends { [key: string]: any },
      TKey extends keyof TObject
    > = Omit<TObject, TKey> &
      {
        [key in TKey]-?: NonNullable<TObject[key]>
      }
    
    function assignDefault<TProps>(
      props: TProps,
      key: keyof TProps,
      value: TProps[typeof key],
    ): asserts props is NonNullableKey<TProps, typeof key> {
      props[key] ??= value
    }
    
    const Component = (props: {
      size?: 'small' | 'medium' | 'large'
    }) => {
      assignDefault(props, 'size', 'medium')
      // From this line, size is not considered optional anymore
      // ...
    }
    Reacted by Pavel Kolář
  21. kolpav commented on Jun 27, 2021

    @kolpav
    Pavel Kolář (kolpav)
    on Jun 27, 2021
    More actions

    Gustavo (@gustavopch) Is exactly something I was trying to avoid

    I know there are ways how to get around that but they are usually lot of code and not as readable and nice as props.size ??= 'base'

    Your solution looks nice but in my case I am not willing to write code doing "nothing" just to please our type checking overlords. TS usually forces you to write better code but this time it doesn't seem to be the case.

    Reacted by Gustavo
    Reacted by Gustavo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CommittedThe team has roadmapped this issueThe team has roadmapped this issueES NextNew featurers for ECMAScript (a.k.a. ESNext)New featurers for ECMAScript (a.k.a. ESNext)Effort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".SuggestionAn idea for TypeScriptAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      • TypeScript 4.0.0

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.