2019/10頃に出たTypescript3.7において普段のバージョンアップとは比較にならないぐらい、めちゃくちゃ便利な機能が追加されたので、挙動について調べてみました。
便利なのはOptional Chaining, Nullish Coalescingの2つです。
どちらもJsにおいてコードの可読性が起きる原因だった、null/undefinedチェックを簡単にできるようにする記法です。
Optional Chaining
nullまたはundefinedの可能性がある変数・関数の末尾に ?
演算子をつけることで、
- nullまたはundefinedだった場合 ⇒ 後続処理停止
- 値がある場合 ⇒ 処理継続
できる記法です。
以下例です。
//シンプルな例
let user1: any = {name: 'doyaaaaaken'};
let user2: any = null;
let user3: any = undefined;
console.log(user1?.name); //"doyaaaaaken"
console.log(user2?.name); //"undefined" ちなみにuser2=nullなので`.name`部分は評価されない
console.log(user3?.name); //"undefined" ちなみにuser3=undefinedなので`.name`部分は評価されない
Chainingという名前のとおり1文の中で連鎖させて使うことができるため、特にobject、classのようなネスト構造が深いものに対してシンプルに書けるようになります。
//chainさせている利用例
let users = [
{name: {lastName: 'doyaaaaa', familyName: 'ken', middleName: 'M'}},
{name: {lastName: 'doyaaaaa', familyName: 'ken2'}},
{email: 'test@example.com'},
];
console.log(users[0]?.name?.middleName); //"M"
console.log(users[1]?.name?.middleName); //"undefined" 理由は、users[1]?.nameは存在するが、middleNameは定義されていないため。
console.log(users[2]?.name?.middleName); //"undefined" 理由は、users[2]は存在するがnameは存在しないため。ちなみに`middleName`部分は評価されない。
console.log(users[3]?.name?.middleName); //"undefined" 理由は、users[3]は存在しないため。ちなみに`name?.middleName`部分は評価されない。
なお上と同等の処理は、今までは以下のように書かなければいけませんでしたので、大きな進歩です。
//今までの書き方例
//ちなみにこの条件文は、?演算子の数が1つ増えるごとにチェックが1〜2つずつ増える
let user0MiddleName = (users[0] && users[0].name != null && users[0].name != undefined) ? users[0]?.name?.middleName : undefined;
console.log(user0MiddleName); //"M"
//ちなみに以下のように書くとシンプルに書けますが、javascriptの仕様上、0・NaN・空文字・falseなどもfalse判定されてしまうため、厳密なnull, undefinedチェックにはならない。
//let user0MiddleName = (users[0] && users[0].name) ? users[0].name.middleName : undefined;
あとここまでの例でサラッと使っていますが、変数以外にも関数呼び出し・配列要素へのアクセスにも同じ書き方が利用できます。
//関数呼び出しにおいて使う場合
log?.('log関数が存在する場合のみ出力されるログ');
//配列要素へのアクセスにおいて使う場合
console.log(arr?.[0]);
なおトリビアですが、この機能は16番目に起票されたIssueらしいです(今はもう23,000以上のIssueがある)。
Nullish Coalescing
nullまたはundefinedの可能性がある変数・関数に続けて ??
演算子をつけることで、nullやundefined時に別の値を指定する機能です。
ユースケース的には「デフォルト値」という概念で捉えるとわかりやすいです。
例えば以下のような使い方ができます。
//`localStorage.getItem('themeColor')` はnullの可能性がある
// nullの場合はデフォルト値としてgreenがテーマカラーとなる
let themeColor = localStorage.getItem('themeColor') ?? 'green';
当然ですがこれも Chaining
することができます。
//第一優先はユーザが設定したテーマカラー
//第二優先は会社が設定したテーマカラー
//それらが設定されていなければgreenがテーマカラーとなる
let themeColor = userConfig.themeColor ?? companyConfig.themeColor ?? 'green';
なお、上と同等の処理は今までは以下のように書かなければいけませんでしたので、これも大きな進歩です。
//今までの書き方例
let themeColor: string;
if (userConfig.themeColor !== null && userConfig.themeColor !== undefined) {
themeColor = userConfig.themeColor;
} else {
if (companyConfig.themeColor !== null && companyConfig.themeColor !== undefined) {
themeColor = companyConfig.themeColor;
} else {
themeColor = 'green'
}
}
//ちなみに以下のような `||` 演算子を使うとシンプルに書けますが、空文字''もfalse判定され、厳密なnull, undefinedチェックにはならない。
//let themeColor = userConfig.themeColor || companyConfig.themeColor || 'green';
感想
Kotlin、Swiftなどモダンな言語にもあるnull safetyな機能が加わったのは嬉しい。
特にJSにおいてnull/undefinedチェックは実装を不要に複雑化させる原因だったので、Typescriptの便利さが更に増しそう。
会社のコードベースでもバージョン3.7に上がったので、早速ガンガン使っていきたいところ。