Speed up TypeScript with Jest

Introduction
With the advent of esbuild, the front-end world has become more demanding of speed in the development environment. The rise of vite can be said to be the best of all.
esbuild and swc are written by the fast Go and Rust, and more often omit Typescript type checking. Type checking for tsc is usually done in IDEs and workflows, so by stripping them off, you're specializing in converting to JavaScript as a pure compiler.
Now, when testing Typescript code, it's often ts-jest or babel-jest as a transformer. However, these can slow down the test.
This time, I will show you how to speed up the execution of Jest and realize a fast test.
Conclusion
I will show you about the method first.
yarn add -D jest @swc/jest
{ "transform": { "^.+\\.(t|j)sx?$": "@swc/jest" }}
jest.config.json
Use @swc/jest as the transformer.
Performance comparison
Let's see how much the performance has improved due to the speedup.
Since the performance differs depending on the execution environment, I will compare the results relative to each other.
CommonJS + Javascript
Try the pattern that seems to be the fastest in theory. CommonJS-style JavaScript may be the fastest, as it shouldn't need to be transpiled. (I'm sorry if I made a mistake 🙏)
I'm not interested in the contents of the function, so prepare a suitable function and test it.
exports.add = (a, b) => a + b
index.js
const { add } = require('../src') describe('add', () => { it('should return 2 when it gives 1,1', () => { const result = add(1,1) expect(result).toBe(2) })})
test/index.spec.js
module.exports = { testEnvironment: "node", roots: ["<rootDir>/test/"]};
jest.config.js
Disable the cache and average about 10 times. It is not a strict measurement, but this time it is a speed comparison of each, so I think that a relative comparison can be made by matching the conditions.
for i in {0..9}; do yarn jest --no-cache ; done
Result:
Transformer
Mean(s)
None(CommonJS + JavaScript)
0.512
I will consider this as a standard.
ESM + TypeScript
A pattern that describes TypeScript in ES module format. This is the pattern most of the time you use TypeScript.
export const add = (a: number, b: number): number => a + b
index.ts
import { add } from '../src/' describe('add', () => { it('should return 2 when it gives 1,1', () => { const result = add(1,1) expect(result).toBe(2) })})
test/index.spec.ts
Use ts-jest for transformers
module.exports = { ..., transform: { '^.+\\.tsx?$': 'ts-jest', }};
jest.config.js
Result:
Transformer
Mean(s)
ts-jest
1.660
None(CommonJS + JavaScript)
0.512
It takes about 3 times longer than CommonJS + JavaScript. I want to do something about this.
Use esbuild for transformers
esbuild is a fast bundler written in Go. By default, Bundler has built-in support for parsing TypeScript syntax and discarding type annotations.
yarn add -D esbuild-jest esbuild
module.exports = { ..., transform: { '^.+\\.tsx?$': 'esbuild-jest' }};
jest.config.js
Result:
Transformer
Mean(s)
esbuild-jest
0.373
ts-jest
1.660
None(CommonJS + JavaScript)
0.512
It's amazing speed. Surprisingly faster than CommonJS + JavaScript.
Use swc for transformers
swc is an ultra-fast compiler written in rust. It seems that Deno is also used for deno lint and deno doc.
yarn add -D @swc/jest
module.exports = { ..., transform: { '^.+\\.tsx?$': ['@swc/jest'], }};
jest.config.js
Result:
Transformer
Mean(s)
@swc/jest
0.351
esbuild-jest
0.373
ts-jest
1.660
None(CommonJS + JavaScript
0.512
You can see that both esbuild and swc can be transpiled at incredible speeds. It is not possible to compare the speed of both with this result alone, but it seems that swc is slightly more advantageous when examined. However, esbuild-jest has the advantage that you can optionally change the following items:
interface Options { jsxFactory?: string jsxFragment?: string sourcemap?: boolean | 'inline' | 'external' loaders?: { [ext: string]: Loader } target?: string format?: string}
Also, for VSCode, an extension for jest is provided. This will automatically run the test in the background if there is a change in the test target, of course this test will also be faster.
If the test is successful, it will nark: white_check_mark: to the target code. Since the test ends immediately, it is a good development experience to mark it immediately.
Stop to use jest.config.ts
The above result used jest.config.js as the jest config file. However, the .ts format configuration file does not improve performance much even if the transformer is changed.
// @swc/jest + jest.config.jsTest Suites: 1 passed, 1 totalTests: 1 passed, 1 totalSnapshots: 0 totalTime: 0.385 sRan all test suites.✨ Done in 1.24s. // @swc/jest + jest.config.tsTest Suites: 1 passed, 1 totalTests: 1 passed, 1 totalSnapshots: 0 totalTime: 0.389 sRan all test suites.✨ Done in 2.93s.
Just changing the file format to .ts has taken more than twice as long.
This is because jest is requesting ts-node from the transpiling of jest.config.ts. Therefore, if possible, you should write the configuration file in json format as jest.config.json, or write the configuration file in jest.config.js format.
Cons
As mentioned at the beginning, esbuild and swc omit type checking and enjoy speed. Therefore, the following code cannot detect compilation errors during testing.
export const add = (a: number, b: string): number => a + b
In this case, the annotations are inappropriate, but when compiled into JavaScript, it works and passes the test.
That said, most IDEs should display the error visually, and you can prevent it by adding tsc to your workflow.