typescriptのv5.6がリリースされましたね。
v5.6リリースで追加された新機能や変更された仕様など、ポイントをかいつまんでまとめてみました。
公式のリリースノートはこちら↓
tsc –buildでエラー時にビルド中断しない
今までは、tsc –build の実行時はすべての対象typescriptプロジェクトのtsconfig.jsonが “noEmitOnError”: true として扱われ、
ビルド最中にどこかしらでエラーが起きた場合はファイルの出力をしない、と言う動きをしていました。
依存関係にある複数のプロジェクトを扱う場合、依存先のプロジェクトがエラーであれば自分のプロジェクトも正常に動作しないため、ファイルの出力をやめてしまうというのはある種正当な動きのように思えますね。
しかし、v5.6からはこの仕様がガラリと変わり、
tsc –buildでビルド実行中にどこかでエラーが発生しても、最後までビルドし続けます。
エラーが発生した箇所のプロジェクトは、ベストエフォート方式でファイルが出力され、ビルドが途中で終了することはありません。
これには、例えばプロジェクトA → プロジェクトBと言う依存関係があり、尚且つプロジェクトBに何かしらのエラーがある場合、プロジェクトAの開発を行ってもいつまで経ってもビルドができない、というような問題をなくそうという意図があるようです。
つまり依存関係にある別プロジェクトのエラー如何によって、目的のプロジェクトの開発/ビルドに支障が出るのを防ぎたい
ということでしょう。
では今までのnoEmitOnErrorのように、エラー発生時点でビルド中止したい場合はどうすれば良いのか?ですが、
`stopOnBuildErrors` という新しいフラグが使えます。
なお、 tsc –build 実行時は incremental/composite フラグの設定に関係なく、常に .tsbuildinfo ファイルを出力するようになります。
ちなみに
.tsbuildinfo ファイルは、TypeScriptのインクリメンタルビルド(増分コンパイル)の情報を保存するために使用されます。
このファイルは通常、ビルド時間の短縮を目的として、以前のコンパイルのキャッシュ情報を格納します。TypeScriptプロジェクトのtsconfig.jsonで incremental フラグを有効にすると、コンパイルの際に .tsbuildinfo ファイルが生成されます。
このファイルには、ソースファイルやその依存関係に関するメタデータが含まれており、再コンパイルが必要かどうかを判断するために使われます。そのため、.tsbuildinfo ファイルをバージョン管理に含める必要はなく、通常は .gitignore に追加して管理から除外します。
常にtrueな条件はエラーに
以下のようなif文を見て下さい。
if (/0x[0-9a-f]/) {
// ...
}
if (x => 0) {
// ...
}
これらはいずれも、ifの条件式が常にtrueを返す文です。
一つ目は正規表現のパターンマッチを変数に適用したかったのでしょう。また二つ目は、〜以上を示す不等号の「>=」を、順番を間違って書いてしまった模様です。
このどちらも、コーディングのミスなのですが、プログラマーの意図に反して常にtrueを返してしまう、という問題があります。
一方このような↓コードでは、作者はnumに値が入っていないケースで actualValue < 100 という式が評価されるような想定をしていそうですが
実際は actualValue < num が先に評価されるので、?? の左辺は常にnot null な状態になります。つまり、右辺の100が返されることは絶対にありません。
actualValue < num ?? 100;
v5.6では、上記のような、常にtrueを返す条件式やnullish checkは、コンパイル時にエラーとなります。
上記のようなコーディングミスは思わぬバグを生む要因になってしまうので、この仕様変更は有り難そうです。
例外的に、true, false, 0, 1の条件は、エラーにはなりません。
これらは明示的に無限回のループを表現する際に使われたりするので、上記のようなパターンとは違って必要なものだからです。
例えば↓のような使われ方はよくしますね。
// こういうケースではエラーは出ない
while (true) {
....
if (isEnd()) {
break;
}
}
無効なモジュール識別子のサポート
つまりどういう意味なのかいまいち分かりにくいですが、
要は
「変数名や関数名として通常使えないものを、文字列リテラルとしてエクスポートできる」
ということです。
例えばこんな感じで↓、絵文字を文字列リテラルとして扱い、エクスポート/インポートして取り扱ったりできます。
const dog = "?";
// ? says bow-wow
console.log(dog, "says bow-wow");
export { dog as "?" };
インポート側で、これを通常の変数に格納することもできます。
import { "?" as dog } from './dog.js';
// ? says woof
console.log(dog, "says woof");
noUncheckedSideEffectImports フラグ
Javascriptにはサイドエフェクトインポートという仕組みがあり、モジュール内の関数や値を明示的に使わなくても、インポートするだけで何かしらの処理が実行されるようなインポートのやり方をさします。
モジュールをインポートするだけで、そのモジュール内部の処理を実行だけさせる、ということです。
これはプロジェクトのグローバルな設定の初期化処理だったり、ポリフィルのためによく用いられるやり方です。
// インポートすることで、そのモジュールの処理を実行させる
import './dir/side_effect_import.js';
v5.6 では noUncheckedSideEffectImports フラグをONにすることで、サイドエフェクトインポートの時にパスの間違いなどでファイルが取得できない場合にエラーを発生させます。
このフラグをONにするが、特定の(e.g. css)ファイル名だけは取得できなくてもエラーを発生させたくない、という場合は、以下のようにアンビエントモジュール宣言を行うことでエラーを回避できます。
// globals.d.ts
declare module "*.css" {}
noCheck フラグ
このフラグをONにすると、ファイルの型チェックがスキップされます。
型がチェックされないとなると、Typescriptの大きな利点が一つ失われそうですが、メリットもいくつかあります。
利用シーンその1. ビルド速度の向上
型チェックはリソースを多く消費する処理なので、スキップすることでビルド時間の大幅な短縮につながります。
例えば、開発段階や小規模なテスト段階で、高頻度にビルド、動作確認を繰り返す場合などは、このフラグをONにしてより効率的に作業を進めることができます。
利用シーンその2. 既存コードベースの移行
Typescriptを導入したてのプロジェクト、Javascriptからの移行を始めたばかりのプロジェクトでは、段階的にコードに型を適用していきます。
このような初期の段階では、あえて型チェックを無効にすることで型適用の範囲拡大をスムーズにしていける可能性があります。
利用シーンその3. 外部ツールや設定の影響で型チェックが不要な場合
エディタやtypescript以外のツールで型チェックが行われており、typescriptで型をチェックする必要がない場合も、不要な型チェックを除くことでビルドを最速化できます。
上記に挙げたのはあくまで一例ですが、このような場合に型チェックをスキップするという選択肢がnoCheckフラグで取れるようになり、より無駄のないビルドが実現できるようになります。
まとめ
以上、Typescript v5.6の主要ポイントのざっくりまとめとなります。
触れていない新仕様もいくつかありますが、そこはご容赦ください。
詳細は公式のリリースノートを参照いただければと思います。↓
コメント