Assertion Functions を本番で実行させない TypeScript Plugin を作った

これは TypeScript Advent Calendar 2019 の 17 日目の代理投稿です。

以前 Assertion Functions を用いて null チェックする記事を書きました。

TypeScript 3.7 で追加された Assertion Functions を使って null チェックを楽にする

が、 assert なのに本番環境で実行されてしまう、という気持ちの悪さを感じていたのです。そんな折、前回の技術書典で手に入れた戦利品の中に TypeScript Compiler API に関するものがあり、時間が作れたので読んでみたのでした。まさにやりたいことが書いてあり、このもやもやを解決できる!と確信したのでざっと組んでみました。

https://www.npmjs.com/package/typescript-plugin-unassert

手元のプロジェクトではうまく動いているようです。

使い方

webpack が必要です。 ts-loader と一緒にインストールしてください。

[sh] yarn add --dev ts-loader typescript-plugin-unassert [/sh]

webpack.config.js に次のように書きましょう。

const unassertTransformer = require('typescript-plugin-unassert')
 
// snip
 
module.exports = {
  // snip
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        options: {
          getCustomTransformers: () => ({
            before: [unassertTransformer],
          }),
        },
        exclude: /node_modules/,
      },
    ],
  },
  // snip
}

これで使えるようになります。

注意点がひとつあります。対象プロジェクトの tsconfig.json に指定している "module""CommonJS" だと、使わなくなった assertassertIsDefined の定義がバンドルされたままとなってしまいます。これは webpack 側の限界なので、容量がシビアな場合は "ES2015" などそれ以外のものを指定してください。

所感

最初はてっきり tsc コマンドに処理をフックする方法が提供されているのかと思ったのですが、そうではなく ts-loader など TypeScript を読み込む責務を持つものが tsc ライクな処理 + フックする方法を実装している、ということのようです。

typescript-plugin ではじまる名前は同様の誤解を与えかねないと思ったのですが、 TypeScript Compiler API を用いてコードを変換するものがすでにあり、その名前を真似しました。命名規則が同じほうがユーザーはわかりやすいだろうと考えたためです。

また、 TypeScript Compiler API は undocumented ですし、突然使えなくなることもあるでしょう。そういった意味でこのプラグインに頼り切るのは早計でしょう。

まとめ

TypeScript Compiler API は整備されており、さくっと効果を発揮できる良いものです。いまだ非公式の扱いであることにのみ気をつければ取り入れていっても良いでしょう。

また、このプラグインがあれば TypeScript における契約による設計も捗るでしょう。ぜひ、より良い設計の一助としてください。