This article has been translated on the basis of machine translation. If there are any mistakes, please fix it.pull request

Speed ​​up TypeScript with Jest

Learn how to speed up your tests with Jest. Introducing esbuild or swc as transformers, it speeds up testing by speeding up transpiling, which tends to be slow with TypeScript.

4/1/20226 min read
..
hero image

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.

1
yarn add -D jest @swc/jest
bash
1
2
3
4
5
{
"transform": {
"^.+\\.(t|j)sx?$": "@swc/jest"
}
}
jest.config.jsonjson

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.

1
exports.add = (a, b) => a + b
index.jsjs
1
2
3
4
5
6
7
8
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.jsjs
1
2
3
4
module.exports = {
testEnvironment: "node",
roots: ["<rootDir>/test/"]
};
jest.config.jsjs

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.

1
for i in {0..9}; do yarn jest --no-cache ; done
bash

Result:

TransformerMean(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.

1
export const add = (a: number, b: number): number => a + b
index.tsts
1
2
3
4
5
6
7
8
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.tsts

Use ts-jest for transformers

1
2
3
4
5
6
7
module.exports = {
...,
transform: {
'^.+\\.tsx?$': 'ts-jest',
}
};
jest.config.jsjs

Result:

TransformerMean(s)
ts-jest1.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.

1
yarn add -D esbuild-jest esbuild
bash
1
2
3
4
5
6
module.exports = {
...,
transform: {
'^.+\\.tsx?$': 'esbuild-jest'
}
};
jest.config.jsjs

Result:

TransformerMean(s)
esbuild-jest0.373
ts-jest1.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.

1
yarn add -D @swc/jest
bash
1
2
3
4
5
6
module.exports = {
...,
transform: {
'^.+\\.tsx?$': ['@swc/jest'],
}
};
jest.config.jsjs

Result:

TransformerMean(s)
@swc/jest0.351
esbuild-jest0.373
ts-jest1.660
None(CommonJS + JavaScript0.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:

1
2
3
4
5
6
7
8
9
10
interface Options {
jsxFactory?: string
jsxFragment?: string
sourcemap?: boolean | 'inline' | 'external'
loaders?: {
[ext: string]: Loader
}
target?: string
format?: string
}
ts

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// @swc/jest + jest.config.js
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.385 s
Ran all test suites.
✨ Done in 1.24s.
// @swc/jest + jest.config.ts
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.389 s
Ran all test suites.
✨ Done in 2.93s.
bash

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.

1
export const add = (a: number, b: string): number => a + b
ts

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.


Edit this page on GitHub

Other Article

Comments