TypeScript 3.7.2がリリースされました!Optional ChainingやNullish Coalescingをさっそく使ってみた

こんにちは。サービスグループの武田です。

日本時間の11月7日0時過ぎにTypeScriptのメジャーアップデート版となる3.7.2がリリースされました!

Release TypeScript 3.7.2 · microsoft/TypeScript

さまざまな新機能やバグフィックスが含まれていますが、個人的には Optional ChainingNullish Coalescing のサポートが非常にうれしいです。詳細は公式ドキュメントを参照してください。

TypeScript 3.7 · TypeScript

このエントリでは3.7.2でサポートされた新しいシンタックスをさっそく使ってみました。

検証環境

環境/サンプルコードとしては、先日のDevelopers.IO 2019 TOKYOでの発表で使用したものと同じです。環境構築から試してみたいという方はぜひスライドを参考にやってみてください。

Developers.IO 2019 in TOKYO で「claspではじめるサーバーレス開発 Google Apps Scriptで簡単自動化」を話しました #cmdevio

claspを使用してGASにpushしていますが、もちろんTypeScriptの新機能を試すだけであれば必須ではないです。スライドのサンプルコードを書いているときに、早くリリースされないかなと思っていたので、ちょうどいい機会ということで書き直した次第です。

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.6
BuildVersion:	18G1012

$ node -v
v10.16.3

$ npx clasp -v
2.3.0

まずはTypeScriptのアップデート

claspをインストールすると自動的にTypeScriptもインストールされますが、執筆時点では3.6.4でした。そのため別途最新のTypeScriptをインストールします。

$ npm install -D typescript

npm WARN inquirer-autocomplete-prompt@1.0.1 requires a peer of inquirer@^5.0.0 || ^6.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN gmail-filter-list@1.0.0 No description
npm WARN gmail-filter-list@1.0.0 No repository field.

+ typescript@3.7.2
updated 1 package and audited 458 packages in 4.13s
found 0 vulnerabilities

$ npx tsc --version
Version 3.7.2

大丈夫そうです。

Visual Studio Code(VS Code)の設定変更

使用するエディタ固有の話になりますが、VS Codeを使用する場合は、使用するTypeScriptのバージョンを変更しないとエラーになりました。ワークスペース固有のバージョンを使用する設定をします。

まずはエディタの右下に表示されているTypeScriptのバージョン(ここでは3.6.3)をクリックします。

そうすると変更パネルが表示されるので、3.7.2を選択します。これで設定は完了です。

Optional ChainingとNullish Coalescingを使ってみる!

先に変更後のソースコード全体を掲載します。

function outputGmailFilterSettings() {
  const sheet = SpreadsheetApp.getActiveSheet();
  sheet.clear();

  const filters = Gmail.Users.Settings.Filters.list('me').filter;
  const headers = [
    'id',
    'criteria.from',
    'criteria.to',
    'criteria.subject',
    'criteria.query',
    'criteria.negatedQuery',
    'criteria.hasAttachment',
    'criteria.excludeChats',
    'criteria.size',
    'criteria.sizeComparison',
    'action.addLabelIds',
    'action.removeLabelIds',
    'action.forward',
  ];
  const values = filters.map(f =>
    [
      f.id,
      f.criteria.from,
      f.criteria.to,
      f.criteria.subject,
      f.criteria.query,
      f.criteria.negatedQuery,
      f.criteria.hasAttachment,
      f.criteria.excludeChats,
      f.criteria.size,
      f.criteria.sizeComparison,
      f.action.addLabelIds?.join(),
      f.action.removeLabelIds?.join(),
      f.action.forward,
    ].map(v => v ?? '')
  );

  const data = [headers, ...values];
  sheet
    .getRange(1, 1, data.length, headers.length)
    .setWrapStrategy(SpreadsheetApp.WrapStrategy.CLIP)
    .setValues(data);
}

diffを取ると次のようになります。

@@ -30,10 +30,10 @@
       f.criteria.excludeChats,
       f.criteria.size,
       f.criteria.sizeComparison,
-      f.action.addLabelIds != null ? f.action.addLabelIds.join() : '',
-      f.action.removeLabelIds != null ? f.action.removeLabelIds.join() : '',
+      f.action.addLabelIds?.join(),
+      f.action.removeLabelIds?.join(),
       f.action.forward,
-    ].map(v => v == null ? '' : v)
+    ].map(v => v ?? '')
   );

   const data = [headers, ...values];

?.がOptional Chainingです。これまではnull/undefinedチェックをしてからプロパティにアクセスする必要がありましたが、シンプルに書けています。??がNullish Coalescingです。値が存在すればその値、null/undefinedならデフォルト値という処理をスマートに書けます。すばらです!

GASで実行するためにpushします。

$ npx clasp push

GASにpushされたコードは次のようになっていました(該当部分のみ抜粋)。

var values = filters.map(function (f) {
    var _a, _b;
    return [
        f.id,
        f.criteria.from,
        f.criteria.to,
        f.criteria.subject,
        f.criteria.query,
        f.criteria.negatedQuery,
        f.criteria.hasAttachment,
        f.criteria.excludeChats,
        f.criteria.size,
        f.criteria.sizeComparison,
        (_a = f.action.addLabelIds) === null || _a === void 0 ? void 0 : _a.join(),
        (_b = f.action.removeLabelIds) === null || _b === void 0 ? void 0 : _b.join(),
        f.action.forward,
    ].map(function (v) { return (v !== null && v !== void 0 ? v : ''); });
});

実行結果はスライドに掲載しているものと同じなのでここでは省略します(ちゃんと実行できました)。

まとめ

個人的にとてもうれしいアップデートとなりましたが、この機能を待ち望んでいた方も少なくないのではないでしょうか?それではよいTypeScriptライフを!