GeezerVison is showing github.com — Nullish Coalescing and Logical Compound Assignments (`??=`, `||=`, `&&=`) · Issue #37255 · microsoft/TypeScript
Nullish Coalescing and Logical Compound Assignments (??=, ||=, &&=) #37255DescriptionSearch 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 ActivityIllusionMH commented on Mar 6, 2020
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 danielrentz commented on Mar 6, 2020 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! DanielRosenwasser commented on Mar 6, 2020 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 changed the title [-]Nullish coalescing assignment (??=)[/-] [+]Nullish Coalescing and Logical Compound Assignments (??=, ||=, &&=)[/+] on Mar 6, 2020changed the title [-]Nullish Coalescing and Logical Compound Assignments (??=, ||=, &&=)[/-] [+]Nullish Coalescing and Logical Compound Assignments (`??=`, `||=`, `&&=`)[/+] on Mar 6, 2020Kingwl commented on Mar 31, 2020 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 DanielRosenwasser commented on Mar 31, 2020 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
- 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 DanielRosenwasser commented on Mar 31, 2020 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 Kingwl commented on Apr 1, 2020 Yes, I saw your approve at tc39😳 aminpaks commented on Jun 16, 2020 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 Kingwl commented on Jun 16, 2020 kolpav commented on Feb 16, 2021 Reacted by Gustavo and Gabriel danielrentz commented on Feb 16, 2021 kolpav commented on Feb 16, 2021 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. gustavopch commented on Jun 24, 2021 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
// ...
}kolpav commented on Jun 27, 2021 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
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|