TypeScript 3.7の新機能
TypeScriptは2019/10/18時点で3.7のベータを公開中です。
正式リリースはロードマップによれば11月とのことです。
今回はもうすぐリリースされる3.7の予習も兼ねて、新機能を紹介していきます。
この記事はAnnouncing TypeScript 3.7 Beta | TypeScriptを元に書いています。
なお、英語の成績が万年赤点だったため、おかしな意訳をしているかも知れません。
その場合はやさしく教えて下さい。
※社内向けでもあったりするので、弊社であまり使わなさそうな部分はさらっと流しています。
Optional Chaining
Optional Chainingがついに来ました。
以下のようなものです。
1 2 |
let x = foo?.bar?.baz(); |
kotlin
やSwift
にはすでにあったりして、さっさとTypeScriptにも入れてくれ〜と思っていた機能がついに追加されます。
これは待ち望んでいた人も多いのでは。
Angularのテンプレート内では、とっくの昔にこのOptional Chainingが使えるのですが、TypeScriptでは使えないためムカついていたので喜ばしい限りです。
Nullish Coalescing
これも今どきの言語では導入されていますね。
以下のようなものです。
1 2 |
let x = foo ?? bar(); |
いままでは ||
を使って似たようなことをすることが多かったと思いますが、ご存知の通り以下のようなバグを生みがちです。
1 2 3 |
// fooになにも渡されなければデフォルト値を設定したいという意図のコード let x = foo || 0.5; // foo=0としたとき、xには0を代入したいのに、0はfalse扱いとなるので、結果0.5が代入されてしまう |
これを回避するためにも、3.7からは積極的にこのNull Coalescingを使用しましょう。
Assertion Functions
例外がスローされなければ、それ以降はある型として扱うものです。
とりあえず、コードを見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function assertIsString(val: any): asserts val is string { if (typeof val !== "string") { throw new AssertionError("Not a string!"); } } const val: any; // 何が入るかは実行時までわからない変数 assertIsString(val); // 例外がスローされていないため、以降はvalの型はstringとして扱われる // そのため、当然以下のようなタイポはビルド時にエラーとなる val.toUpppercase(); |
Type Guardと似てますが、Type Guardは返り値がtrue
の場合、その型として扱うというものだったため、必ず条件式が必要になっていました。
このAssertion Functionsが導入されれば、実行時の引数型チェックがより簡素に記述できそうです。
Recursive Type Aliases
再帰的なType Aliasesの定義が可能になります。
複雑な型定義を書いたことがある人ならピンと来るかも知れませんが、3.6までは以下のようなJson
Type Aliasesを定義したい場合、追加でJsonObject
とJsonArray
というインターフェースも定義する必要がありました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
type Json = | string | number | boolean | null | JsonObject // { [property: string]: Json } とか書きたいけど、再帰参照になるのでエラーとなる | JsonArray; // Array<Json>と書きたいけど、再帰参照になるのでエラーになる // 以下のインターフェースを定義すればエラーを回避できるため、3.6まではみんなそうする必要があった interface JsonObject { [property: string]: Json; } interface JsonArray extends Array<Json> {} |
これが3.7からはこうなります。
コンパイラが型解決を少し遅延して(little bit “lazier”)、このパターンも許可されるようになりました。
1 2 3 4 5 6 7 8 |
type Json = | string | number | boolean | null | { [property: string]: Json } | Json[]; |
ちなみに、当たり前ですが以下の再帰Type Aliasesはエラーになります。
1 2 |
type Foo = Foo; |
Uncalled Function Checks
しょうもないミスとしてありがちな、メソッド呼び出しを忘れてif文に書いてしまうようなことを防いでくれる機能です。
以下のようなものですね。
1 2 3 4 5 6 7 8 9 10 11 12 |
interface User { isAdmin(): boolean; } function doAdminThing(user: User) { // メソッドなのに呼び出し忘れてしまい、常に`true`の扱いとなってしまう! if (user.isAdmin) { // 管理者しか実行してはいけないなにか // 常にtrueなので誰でも実行できてしまう } } |
これが3.7からはちゃんと怒ってくれるようになります。
1 2 3 4 5 6 |
function doAdminThing(user: User) { if (user.isAdmin) { // ~~~~~~~~~~~~~~~~~~~~ // error! This condition will always return true since the function is always defined. // Did you mean to call it instead?t |
@ts-nocheck in TypeScript Files
多用されると困りますが、今までJavaScriptファイルだけだった// @ts-nocheck
をTypeScriptファイルでも使用できるようになりました。
TypeScriptファイルの先頭に// @ts-nocheck
を追加すれば型チェックなどが無効になります。
Breaking Changes
DOM changes
lib.dom.d.ts
が更新されました。
主にnull許容に関連する変更らしいですが、Breaking Changesとなっているので、もしかしたら影響があるかも知れません。
Function Truthy Checks
Uncalled Function Checks
でも説明した通り、ifでメソッドが呼び出されていない場合、TypeScriptでエラーが発生するようになりました。
Local and imported Type Declarations Now Conflict
これまでは以下のように、インポートした型定義と同一名称の型定義を定義できていましたが、これはどうやらバグだったらしく、今後はこのようなケースはTypeScriptでエラーを吐くようになります。(バグだったの??)
もしなんらかの意図を持って名前を重複させていた場合は修正が必要になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// ./someOtherModule.ts export interface SomeType { y: string; } // ./myModule.ts import { SomeType } from "./someOtherModule"; export interface SomeType { x: number; } function fn(arg: SomeType) { console.log(arg.x); // Error! 'x' doesn't exist on 'SomeType' } |
その他
他にも以下のアップデートがあります。
弊社ではあまり使い所がなさそうなので一覧だけ紹介します。
- 返り値neverの関数のサポート改善 (Better Support for never-Returning Functions
- JavaScriptソースコードが混在する場合の.d.ts生成 (–declaration and –allowJs)
- プロジェクト参照によるビルドフリー編集 (Build-Free Editing with Project References)
- 末尾のセミコロンが必須ではない場所でのセミコロン挿入・削除サポート (Semicolon Formatter Option)
参考
Announcing TypeScript 3.7 Beta | TypeScript