Jest’s default transformer is great for most things, but when you need to transpile modern JavaScript or TypeScript features, you’ll find yourself reaching for Babel.
Here’s a typical Jest setup that uses Babel to handle your .js and .ts files:
// jest.config.js
module.exports = {
transform: {
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
},
testEnvironment: "node",
};
And here’s a babel.config.js that will get you started, enabling common ES features and TypeScript support:
// babel.config.js
module.exports = {
presets: [
["@babel/preset-env", { targets: { node: "current" } }],
"@babel/preset-typescript",
],
};
With this configuration, Jest will now use Babel to process all .js, .jsx, .ts, and .tsx files before running tests. This means you can confidently use the latest ECMAScript features like async/await, arrow functions, and spread syntax, as well as all of TypeScript’s type checking and syntax.
Let’s look at a simple example. Suppose you have a file math.ts:
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
export const multiply = (a: number, b: number): number => a * b;
And a test file math.test.ts:
// math.test.ts
import { add, multiply } from './math';
describe('math', () => {
test('adds two numbers', () => {
expect(add(1, 2)).toBe(3);
});
test('multiplies two numbers', () => {
expect(multiply(2, 3)).toBe(6);
});
});
When you run npm test (or yarn test), Jest, powered by Babel, will:
- Identify Test Files: Jest scans your project for files matching the
*.test.{js,jsx,ts,tsx}or*.spec.{js,jsx,ts,tsx}pattern. - Transformer Application: For each identified test file (and the source files it imports), Jest checks its
jest.config.js. It sees that files matching^.+\\.(js|jsx|ts|tsx)$should be processed bybabel-jest. - Babel Transformation:
babel-jestinvokes Babel with the configuration frombabel.config.js.@babel/preset-envwithtargets: { node: "current" }ensures that JavaScript code is transpiled down to a version compatible with the Node.js environment Jest is running in. This handles modern syntax like=>orasync/await.@babel/preset-typescripttells Babel how to parse and transpile TypeScript syntax, including types and interfaces, into plain JavaScript.
- Execution: The transpiled JavaScript code is then executed by Jest’s test runner.
The surprising thing about this setup is how seamlessly Babel integrates. You don’t typically write Babel plugins for Jest; you configure Babel itself, and babel-jest acts as the bridge. This means you can use the exact same Babel configuration for your application code as you do for your test code, ensuring consistency.
The targets: { node: "current" } option in @babel/preset-env is particularly useful. It tells Babel to transpile your code to match the features of the currently running Node.js version. This is generally a safe bet for Jest, as it runs in a controlled Node.js environment. If you needed to support older Node.js versions for your application, you would adjust this target accordingly, and Jest would also benefit from that broader compatibility.
The power here is that you can leverage any Babel plugin or preset you’d normally use for your application. Want to use decorators? Add @babel/plugin-proposal-decorators. Need experimental features? Configure them. Jest will just work with them because Babel handles the heavy lifting of understanding and transforming the syntax.
Consider the case where you have a .babelrc or babel.config.js file already present in your project for your application’s transpilation. Jest will pick this up automatically if you don’t specify a transform option in jest.config.js. However, explicitly defining the transform option like we did is generally more robust, ensuring that Jest always uses Babel for your JavaScript and TypeScript files, regardless of other Babel configurations in your project.
The most common pitfall is forgetting to install the necessary Babel packages: @babel/core, @babel/preset-env, @babel/preset-typescript, and babel-jest. If you run Jest and get errors about unknown syntax or syntax not supported by the Node.js version, it’s almost always a missing Babel dependency or an incorrect Babel configuration.
If you’re using Jest with a framework like Create React App or Next.js, this Babel integration is often handled for you out-of-the-box. You’d typically only need to customize the babel.config.js or .babelrc if you need specific plugins or presets beyond the defaults.
The next step after mastering Babel integration is often exploring Jest’s mocking capabilities to isolate components and functions during testing.