TypeScript 3.7から利用できるOptional Chainingについて

Tags:

#TypeScript #JavaScript
このエントリーをはてなブックマークに追加

半年ほど前にTypeScript 3.7がリリースされ、Optional Chainingが利用できるようになりました。 仕事でもよく利用しており、とても便利なので紹介したいと思います。

nullチェック、undefinedチェック

TypeScriptのコードを書いていると、nullチェック、undefinedチェックをするコードはたびたび頻出します。 例えば以下のようなFooという型があったとします。 Foo型にはオプショナルなbarというプロパティがあり、barの中にはオプショナルなbazというプロパティがあるとします。

type Foo = {
  bar?: {
    baz?: string;
  };
};

barやbazはオプショナルなプロパティであるため、そのプロパティがあっても、なくても問題はありません。 そのため以下の全てのパターンをFoo型として定義できます。

const a: Foo = {};
const b: Foo = {
  bar: {},
};
const c: Foo = {
  bar: {
    baz: "hoge",
  },
};

このFoo型を使った関数を考えてみます。Foo型の引数fooを受け取り、foo.bar.bazが存在する場合はその文字列を出力するprintBaz関数を考えてみます。 パッと直感的に書くと、以下のように書いてしまうかもしれません。

// コンパイルエラー
function printBaz(foo: Foo) {
  if (foo.bar.baz) {
    console.log(foo.bar.baz);
  }
}

しかしtsconfig.jsonのコンパイラオプションで"strictNullChecks": trueにしている場合、この関数はコンパイルが通りません。

- error TS2532: Object is possibly 'undefined'.
   if (foo.bar.baz) {
       ~~~~~~~
エラーメッセージにはObject is possibly 'undefined'.と書かれています。 Foo型のbarプロパティはオプショナルであるため、undefinedの可能性があります。 例えば、const a: Foo = {};をprintBaz関数に渡してしまうと、barのプロパティがないので、Cannot read property 'baz' of undefinedのエラーが出てしまいます。 TypeScriptはこのエラーが発生しないように、nullチェック、undefinedチェックをしろと注意してくれているのです。

TypeScript 3.7以前ではこのコンパイルエラーを回避するために以下のようなコードを書かなくてはなりませんでした。

function printBaz(foo: Foo) {
  if (foo.bar && foo.bar.baz) {
    console.log(foo.bar.baz);
  }
}

この関数のif (foo.bar && foo.bar.baz) {の部分に注目してください。 このif文の条件式はfoo.barがTruethyでかつfoo.bar.bazがTruethyであるかを確認しています。 このif文によって、foo.bar.bazは必ず存在すると型推論され、ifのブロック内でconsole.log(foo.bar.baz);と書いてもコンパイラには怒られません。

参考:Truethyについて

ただ、このif文はオプショナルなプロパティが増えるほど、長くなってしまいます。もし仮にFooが以下のような定義だったら、bazを表示するにはif文をif (foo.hoge && foo.hoge.bar && foo.hoge.bar.baz) {と書かなくてはなりません。

type Foo = {
  hoge?: {
    bar?: {
      baz?: string;
    };
  };
};

Optional Chaining

この問題を解決してくれるのがOptional Chainingです。 Optional Chainingを使うとprintBaz関数は以下のように書けます。

function printBaz(foo: Foo) {
  if (foo.bar?.baz) {
    console.log(foo.bar.baz);
  }
}

Optional Chainingでは?のオペレーターを利用します。 bar?.bazのように、オプショナルなプロパティに対して、?.を追加することによって、プロパティがnullかundefinedの場合は次のプロパティにはアクセスせず、undefinedを返します。 今回の例ではbarがnullかundefinedであれば、bazにはアクセスせずにundefinedを返します。

Optional Chainingを使うことによってif (foo.bar && foo.bar.baz) {と長くなっていたif文をif (foo.bar?.baz) {のようにすっきり書くことができます。

Optional Chainingの仕様については以下のページを参照してください。 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html https://github.com/tc39/proposal-optional-chaining

まとめ

Optional Chainingを利用することによって、nullチェックやundefinedチェックをすっきりと書くことができます。 自分で書くコードではオプショナルなプロパティを利用することは少ないと思いますが、外部のライブラリを利用すると、どうしてもオプショナルなプロパティで定義されていたりして、nullチェック、undefinedチェックをしないといけない場合があると思います。 そんな時にはぜひ、Optional Chainingを利用してみてください。 以上です。

See Also