LoginSignup
31
21

More than 3 years have passed since last update.

TypeScript 3.7 から導入される予定の Optional Chaining を試す

Last updated at Posted at 2019-09-28

Optional Chaining とは

オブジェクト構造の奥深くにあるプロパティ値を参照する際、途中のプロパティが存在するかどうかのチェックをコードに書かなくても、安全に処理してくれる仕組み。

tc39/proposal-optional-chaining

The Optional Chaining Operator allows a developer to handle many of those cases without repeating themselves and/or assigning intermediate results in temporary variables:

var street = user.address?.street

上の例では、address が undefined であってもエラーは発生せず、street 変数には undefined がセットされるだけとなる。

Optional Chaining が使えない状況では以下のように自前でチェックするコードを書く必要があった。

var street = user.address && user.address.street;

Optional Chaining は TypeScript 3.7 で導入される予定 (2019年11月リリース予定)

TypeScript 3.7 Iteration Plan · Issue #33352 · microsoft/TypeScript

November 5th
TypeScript 3.7 Final Release

Optional Chaining の実行環境を用意する

TypeScript の nightly builds には Optional Chaining は含まれていなかった (2019年9月27日現在)。

そのため TypeScript ソースコードの git リポジトリを clone して、Optional Chaining が組み込まれた optionalChainingStage3 ブランチをビルドする。

$ git clone https://github.com/microsoft/TypeScript.git

$ cd TypeScript

$ git checkout optionalChainingStage3

$ npm install -g gulp

$ npm install

$ gulp local

$ cd ..

$ node TypeScript/built/local/tsc.js --version
Version 3.7.0-dev

TypeScript のソースコードをビルドする方法は公式リポジトリ microsoft/TypeScript: TypeScript is a superset of JavaScript that compiles to clean JavaScript output. の README に載っている。

Optional Chaining を使うソースコードを準備する

以下の「foo?.bar?.baz」的なコードを hello.ts というファイルに保存する。
interface には Optional Properties (存在しない可能性もあるオプション的なプロパティ) なプロパティとして bar と baz を定義している。

interface Foo {
  name: string
  bar?: {
    name: string
    baz?: {
      name: string
    }
  }
}

let foo: Foo

foo = {name: 'a', bar: {name: 'bar', baz: {name: 'baz'}}}
console.log(foo?.bar?.baz)

foo = {name: 'a', bar: {name: 'bar'}}
console.log(foo?.bar?.baz)

foo = {name: 'a'}
console.log(foo?.bar?.baz)

Optional Chaining の挙動を確認する

TypeScript の tsc コマンドで JavaScript のコードに変換する。

$ tsc hello.ts 

hello.js が以下の内容で出力される。

var _a, _b, _c, _d, _e, _f;
var foo;
foo = { name: 'a', bar: { name: 'bar', baz: { name: 'baz' } } };
console.log((_b = (_a = foo) === null || _a === void 0 ? void 0 : _a.bar) === null || _b === void 0 ? void 0 : _b.baz);
foo = { name: 'a', bar: { name: 'bar' } };
console.log((_d = (_c = foo) === null || _c === void 0 ? void 0 : _c.bar) === null || _d === void 0 ? void 0 : _d.baz);
foo = { name: 'a' };
console.log((_f = (_e = foo) === null || _e === void 0 ? void 0 : _e.bar) === null || _f === void 0 ? void 0 : _f.baz);

Node.js で実行する。

$ node hello.js
{ name: 'baz' }
undefined
undefined

チェーン途中のオブジェクトに undefined が存在しても、Cannot read property などのエラーが発生せずに実行できている。

Optional Chaining が使えない場合には危険なコードを書いてしまう

Optional Chaining が使えない場合は、辿る変数が undefined かどうかチェックするのが手間なので、省略してしまって以下のような危険なコードを書きがち。

let foo: Foo

foo = {name: 'a', bar: {name: 'bar', baz: {name: 'baz'}}}
console.log(foo.bar.baz)

foo = {name: 'a', bar: {name: 'bar'}}
console.log(foo.bar.baz)

foo = {name: 'a'}
console.log(foo.bar.baz)

これを実行すると bar が undefined の場合に「TypeError: Cannot read property 'baz' of undefined」が発生してしまう。

$ tsc hello.ts

$ node hello.js
{ name: 'baz' }
undefined
/Users/me/hello.js:7
console.log(foo.bar.baz);
                    ^

TypeError: Cannot read property 'baz' of undefined

参考資料

31
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
21