Ruby でお馴染みの演算子 ||= が ES2021 に搭載されていたから使用感レビュー
Ruby の自己代入演算式 ||=
ruby を書く人にはお馴染みのこれ ||=
ES2021 から新しく追加された 代入演算子 (Assignment Operators) : ||=
, &&=
, ??=
は、Ruby にインスパイアされたものらしいです。
TC39 での proposal issue
モチベーション
このようなコードを書くことがありますよね?
function example(opts) {
// Ok, but could trigger setter.
opts.foo = opts.foo ?? 'bar'
// No setter, but 'feels wrong' to write.
opts.baz ?? (opts.baz = 'qux');
}
example({ foo: 'foo' })
これを新しい演算子 ??=
を使って書いてみると、このようにスッキリ書くことができます!
function example(opts) {
// Setters are not needlessly called.
opts.foo ??= 'bar'
// No repetition of `opts.baz`.
opts.baz ??= 'qux';
}
example({ foo: 'foo' })
||=
, &&=
, ??=
代入演算子 (Assignment Operators) : 2021年6月にES2021 が出た
その時に追加された ||=
, &&=
, ??=
対応 node version 15以上
TS 4.0 から
仕様書
論理和代入 (||=)
論理和代入演算子 (x ||= y) は、x が偽値である場合にのみ代入を行います。
この x ||= y という式は、以下のように分解できます。
x = x || y
const user = { name: 'オナマエ', age: 0, message: '' };
user.name ||= 'ワーイ( ^ω^ )ワーイ';
console.log(user.name);
// output: 'オナマエ'
user.age ||= 20;
console.log(user.age);
// output: 20
user.message ||= 'ハロハロ~( ^ω^ )';
console.log(user.message);
// output: 'ハロハロ~( ^ω^ )'
0
, ''
のように 偽値 (false) と判別されるものに代入されているのがわかります。
論理積代入 (&&=)
論理和代入 (||=) の逆版
論理積代入 (x &&= y) 演算子は、x が真値である場合にのみ代入を行います。
const user = { name: 'オナマエ', age: 0, message: '' };
user.name &&= 'ワーイ( ^ω^ )ワーイ';
console.log(user.name);
// output: 'ワーイ( ^ω^ )ワーイ'
user.age &&= 20;
console.log(user.age);
// output: 0
user.message &&= 'ハロハロ~( ^ω^ )';
console.log(user.message);
// output: ''
真値 (true) と判別されるものに代入されているのがわかります
Null 合体代入 (??=)
Null 合体代入 (x ??= y) 演算子は、x が nullish (null または undefined) である場合にのみ代入を行います。
user オブジェクト に存在しない property に対して、 ??=
で代入することができる。
const user = { name: 'オナマエ' , age: null};
user.age ??= 20;
user.message ??= 'メッセージだよ'
console.log(user);
// output: { "name": "オナマエ", "age": 20, "message": "メッセージだよ"}
// null だった age プロパティの値が更新され、存在しなかった message プロパティが入ってる!
??=
と ||=
??
と ||
の違いと同じ。
||
は 0 や '' を falsely と判別してしまうので、x ||= y
だと適さない場合がある
let user1 = {name: null, message: undefined, age: 0}
let user2 = {name: null, message: undefined, age: 0}
user1.name ||= 'name'
user1.message ||= 'message'
user1.address ||= 'address'
user1.age ||= 20
user2.name ??= 'name'
user2.message ??= 'message'
user1.address ??= 'address'
user2.age ??= 20
console.log(user1)
// { "name": "name", "message": "message", "address": "address", "age": 20 }
// null や undefined プロパティの値が更新され、存在しなかった address プロパティが入ってる!
// age の 0 は偽値(falsey)のため、 20 に更新されている
console.log(user2)
// { "name": "name", "message": "message", "address": "address", "age": 0 }
// null や undefined プロパティの値が更新され、存在しなかった address プロパティが入ってる!
// age の値は null or undefined ではないため、 age: 0 のまま
false に変換されうる式の例
- null
- NaN
- 0
- 空文字列 ("" または '' または ``)
- undefined
Null 合体 (??)
左辺が null または undefined の場合に右の値を返し、それ以外の場合に左の値を返します。
console.log(undefined ?? '??'); // "??"
console.log(null ?? '??'); // "??"
console.log('' ?? '??'); // ''
console.log(false ?? true); // false
console.log(0 ?? 1); // 0
論理和 (||)
x || y は、x が true に変換できる場合は x を返し、それ以外の場合は y を返します。
= x が偽値である場合は y を返す
console.log(undefined || '||') // "||"
console.log(null || '||') // "||"
console.log('' || '||') // "||"
console.log(false || true) // true
console.log(0 || 1); // 1
user のデータを更新する関数があるとする
if文を使う書き方
async updateUser(user: User, name: string | undefined, age: string | undefined): Promise<User> {
if (name) {
user.name = name;
}
if (age) {
user.age = age;
}
// user を save する処理
}
||
を使う書き方
論理和
string | undefined
としたとき
引数 name, age は async updateUser(user: User, name: string | undefined, age: string | undefined): Promise<User> {
user.name = name || user.name; // name が undefined のとき user.name に user.nameを代入されたい
user.age = age || user.age; // age が undefined のとき user.age に user.ageを代入されたい
// user を save する処理
}
しかしこれだと、少し問題がありそう。
||
では、name, age が null or undefined 以外の 0 や '' でも falsely になってしまう。
- 引数の name が
''
のとき、user.name には user.name が代入される - 引数の age が
0
のとき、user.age には user.age が代入される
=> 引数が null or undefined の時のみ、判別したい
??
を使う書き方
Null 合体 ??
では、左辺が null or undefined のときのみ、falsely と判別されるため、
- 引数の name が
''
のとき、user.name には name が代入される - 引数の age が
0
のとき、user.age には age が代入される
async updateUser(user: User, name: string | undefined, age: string | undefined): Promise<User> {
user.name = name ?? user.name; // name が undefined のとき user.name に user.nameを代入されたい
user.age = age ?? user.age; // age が undefined のとき user.age に user.ageを代入されたい
// user を save する処理
}