これは、なにをしたくて書いたもの?
最近、TypeScriptを使っているわけですが、テストコードを書くとJestを使います。
Jestのドキュメントに習うとTypeScript+Jestではts-jestを使ってテストを実行することになるのですが、これが遅いのでどうにかならないかな?
ということで調べてみました。
ソースコード本体の方はtsc --watch
でいいかなと思っているのですが、テストコードの方はnpm test
で実行したいなとも思うので…。
Jest+TypeScript
Jestのドキュメントに習うと、TypeScriptでJestを使う場合はBabelまたはts-jestを使うことになります。
Getting Started / Using TypeScript
自分は、ts-jestを使っています。
ts-jest以外の選択肢
esbuildとSWCがあるようです。どちらも、TypeScriptのサポートとJestに対するtransformerがあります。
esbuild。
esbuild - An extremely fast JavaScript bundler
GitHub - aelbore/esbuild-jest: A Jest transformer using esbuild
SWC。
Rust-based platform for the Web – SWC
どちらもまったく知らないので、軽くドキュメントなどを眺めてみます。
esbuild
esbuildは、高速なJavaScriptのバンドラーであると謳っています。Goで実装されているようです。
esbuild - An extremely fast JavaScript bundler
GitHub - evanw/esbuild: An extremely fast JavaScript and CSS bundler and minifier
今のWeb用のビルドツールは10〜100倍遅くなっているため、高いパフォーマンスで、使いやすいバンドラーを作ることがesbuildの目標だと
されています。
Our current build tools for the web are 10-100x slower than they could be. The main goal of the esbuild bundler project is to bring about a new era of build tool performance, and create an easy-to-use modern bundler along the way.
主な機能。
- 高速
- ES6およびCommonJSのサポート
- ES6モジュールのTree shaking
- JavaScriptおよびGo向けのAPI提供
- TypeScript、JSXのサポート
- Source Map
- Minify
- プラグイン
今回の主題である、TypeScriptに関するドキュメントはこちら。
TypeScriptのサポートはデフォルトで有効になっています。
注意点としてはesbuildは型チェックを行わないので、tsc --noEmit
を合わせて使う必要があります。
However, esbuild does not do any type checking so you will still need to run tsc -noEmit in parallel with esbuild to check types.
その他、注意点。
Content Types / TypeScript caveats
- ファイルは独立してコンパイルされる
import
は、ECMAScriptモジュールとして振る舞う- 型システムに関する機能はサポートしていない
tsconfig.json
の特定の項目のみを考慮する*.ts
ファイルにtsx
ローダーを使用できない
Jest向けのTransformerは、こちらがあります。
GitHub - aelbore/esbuild-jest: A Jest transformer using esbuild
SWC
SWCは、Rustで実装された次世代の開発者向けツールとされています。Next.js、Parcel、Denoなどで利用されているのだとか。
Rust-based platform for the Web – SWC
コンパイルおよびバンドルの両方に使用でき、JavaScriptとTypeScriptをサポートしています。
設定は.swcrc
というファイルで行い、
この中にTypeScriptに関する設定項目があります。
Compilation / jsc.parser / typescript
ただ、SWCもesbuildと同様、TypeScriptの型チェックは行わないようです。
TypeScript type checker · Issue #571 · swc-project/swc · GitHub
TypeScript support for type-checking · Issue #126 · swc-project/swc · GitHub
Swc can also compile typescript / tsx to ecmascript. Note that it does not type-check at the time of writing.
Introducing SWC 1.0 / TypeScript support
Jestについてはサポートがあり、Transformerが提供されています。こちらをts-jest
の代わりに利用できます。
説明は、これくらいにして使っていってみましょう。
環境
今回の環境は、こちら。
$ node --version v16.13.1 $ npm --version 8.1.2
お題
簡単なテストプログラムを、以下のバリエーションで書いて確認したいと思います。
- JavaScriptのみ
- TypeScript+ts-jest
- TypeScript+esbuild
- TypeScript+SWC
以下のディレクトリを作り、それぞれ作っていきます。
$ mkdir jest-js jest-ts-jest jest-ts-esbuild jest-ts-swc
JavaScriptのみ
まずは、JavaScriptのみ。
$ cd jest-js
$ mkdir src test
npmプロジェクト作成。
$ npm init -y
Prettierのインストール。
$ npm i -D -E prettier
設定。
.prettierrc.json
{ "singleQuote": true }
Jestのインストール。
$ npm i -D jest
jest --init
はしましたが
$ npx jest --init
結局、内容はこれくらいになりました。
jest.config.js
module.exports = { testEnvironment: "node", };
依存関係。
"devDependencies": { "jest": "^27.4.7", "prettier": "2.5.1" }
src/calc.js
function plus(a, b) { return a + b; } function minus(a, b) { return a - b; } module.exports = { plus, minus };
テストコード。
test/calc.test.js const calc = require('../src/calc'); test('plus', () => { expect(calc.plus(1, 2)).toBe(3); }); test('minus', () => { expect(calc.minus(5, 1)).toBe(4); });
実行時間は、これくらいです。
※なんとなく、jest
コマンドで実行することにしました
$ npx jest PASS test/calc.test.js ✓ plus (2 ms) ✓ minus (1 ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 0.239 s, estimated 1 s Ran all test suites.
ここから、他のケースを見ていきます。
TypeScript+ts-jest
次は、TypeScriptとts-jestで。
$ cd jest-ts-jest
先ほどのnpmプロジェクトのセットアップに、TypeScriptを追加。
$ mkdir src test $ npm init -y $ npm i -D typescript $ npm i -D -E prettier $ npm i -D @types/node@v16
tsconfig.json
{ "compilerOptions": { "target": "esnext", "module": "commonjs", "baseUrl": "./src", "outDir": "dist", "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "esModuleInterop": true }, "include": [ "src" ] }
.prettierrc.json
は、先ほどと同じです。
Jestおよびts-jestのインストール。
$ npm i -D jest @types/jest ts-jest
設定ファイルの作成。
$ npx ts-jest config:init
jest.config.js
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { preset: 'ts-jest', testEnvironment: 'node', };
依存関係。
"devDependencies": { "@types/jest": "^27.4.0", "@types/node": "^16.11.19", "jest": "^27.4.7", "prettier": "2.5.1", "ts-jest": "^27.1.2", "typescript": "^4.5.4" }
src/calc.ts
export function plus(a: number, b: number): number { return a + b; } export function minus(a: number, b: number): number { return a - b; }
テストコード。
test/calc.test.ts
import { plus, minus } from '../src/calc'; test('plus', () => { expect(plus(1, 2)).toBe(3); }); test('minus', () => { expect(minus(5, 1)).toBe(4); });
実行。
$ npx jest PASS test/calc.test.ts ✓ plus (2 ms) ✓ minus (1 ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 1.761 s, estimated 2 s Ran all test suites.
JavaScriptの時より、7倍くらい時間がかかります。
これをなんとかしたい、と。
TypeScript+esbuild
次は、先ほどのプロジェクトをts-jestからesbuildに変更したものを作ります。
$ cd jest-ts-esbuild
セットアップ。
$ mkdir src test $ npm init -y $ npm i -D typescript $ npm i -D -E prettier $ npm i -D @types/node@v16
tsconfig.json
および.prettierrc.json
は、先ほどと同じなので省略。
Jestのインストールの際は、ts-jestを入れないようにします。
$ npm i -D jest @types/jest
esbuildおよびesbuild-jestのインストール。
$ npm i -D esbuild-jest esbuild
Jestの設定。transformer
にesbuild-jest
を指定します。
jest.config.js
module.exports = { testEnvironment: 'node', transform: { "^.+\\.tsx?$": "esbuild-jest" } };
ちなみにJestの設定をTypeScriptでも書けますが、結局遅くなるのでJavaScriptにしています。
依存関係。
"devDependencies": { "@types/jest": "^27.4.0", "@types/node": "^16.11.19", "esbuild": "^0.14.11", "esbuild-jest": "^0.5.0", "jest": "^27.4.7", "prettier": "2.5.1", "typescript": "^4.5.4" }
ソースコードおよびテストコードは、ts-jestの時と同じです。
テストを実行。
$ npx jest PASS test/calc.test.ts ✓ plus (2 ms) ✓ minus Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 0.215 s, estimated 1 s Ran all test suites.
速いです。めちゃくちゃ速いです。JavaScriptのみの時と同等か、それ以上に速いくらい。
すごいですねぇ…。
型チェックが外れているんですが。
TypeScript+SWC
最後に、SWCです。
$ cd jest-ts-swc
プロジェクトのセットアップ。
$ mkdir src test $ npm init -y $ npm i -D typescript $ npm i -D -E prettier $ npm i -D @types/node@v16
tsconfig.json
および.prettierrc.json
は、ここまでと同じ。
Jestのインストール。
$ npm i -D jest @types/jest
SWCのJestモジュールのインストール。
$ npm i -D @swc/jest
Jestの設定。transformer
に@swc/jest
を指定。
jest.config.js
module.exports = { testEnvironment: 'node', transform: { "^.+\\.tsx?$": "@swc/jest", } };
ソースコードおよびテストコードも、ここまでと同じ。
テストを実行。
$ npx jest PASS test/calc.test.ts ✓ plus (2 ms) ✓ minus Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 0.217 s, estimated 1 s Ran all test suites.
こちらも速いです。esbuildと同等ですね。
まとめ
TypeScriptを使ったJestのテスト実行を速くしたいなと思い、esbuildおよびSWCを調べつつ、素のJavaScriptとts-jestとの比較をやってみました。
esbuild、SWC、ともに速いですね。
型チェックをスキップしているのでそこは難点なのですが、それは別途tscでカバーできるように考えてみようかなと思います。
テストコードとはいえ、完全に型チェックを外すと意味ないので…。