Skip to content
intermediate Phase 15 · TypeScript Testing & Tools

Jest with TypeScript (Advanced)

Write and run TypeScript tests with Jest and ts-jest configuration.

1h
0 problems
Topic Progress 0%

ts-jest Configuration

ts-jest Configuration

ts-jest compiles TypeScript during the test run so tests can import TS sources directly:

// jest.config.ts
export default {
  preset: "ts-jest",
  testEnvironment: "node",
  moduleNameMapper: { "^@app/(.*)$": "<rootDir>/src/$1" },
  transform: { "^.+\.tsx?$": ["ts-jest", { tsconfig: "tsconfig.test.json" }] },
};

Using a dedicated tsconfig.test.json lets tests include @types/jest and esModuleInterop without relaxing the app build. Diagnostics come from the same config: turn them off only for transpile-only CI runs, because a skipped diagnostic turns a compile error into a confusing runtime failure.

Mocking with Types

Mocking with Types

jest.mock is hoisted above imports, so the factory runs before the module under test:

jest.mock("../src/mailer", () => ({ send: jest.fn() }));
import { send } from "../src/mailer";

const mockedSend = jest.mocked(send);
mockedSend.mockResolvedValue({ id: "1" });

jest.mocked gives a fully typed mock — .mockResolvedValue and .mock.calls infer from the real signature. For object collaborators use jest.spyOn(svc, "fetchUser") (restores in afterEach with restoreMocks: true), and for paths that should resolve like the app, mirror every alias in moduleNameMapper — missing mappings are the most common "cannot find module" in TS test suites.

Pitfalls and Interview Angles

Pitfalls and Interview Angles

Globals versus imports. Plain describe/expect rely on @types/jest; importing from @jest/globals gives typed symbols without that package. Mixing both in one repo makes one side any.

ESM mode. Running Jest as native ESM needs --experimental-vm-modules and extensionsToTreatAsEsm; the CommonJS transform path is the default and expects jest.mock hoisting rather than vi-style static mocks.

Type-only imports. Under isolatedModules, forgetting import type emits a runtime require of a file that exports nothing.

Interview: explain hoisting (why jest.mock can reference a factory before imports), how jest.mocked preserves types, and when you would choose spyOn over a full module mock.

Quiz

1. Why does jest.mock use a hoisting transformer in ts-jest?

Question 1 options

2. What does jest.mocked(fn) provide that fn alone does not?

Question 2 options

3. A test compiles but fails with "Cannot find module '@app/config'". What is missing?

Question 3 options

Flashcards

Question

ts-jest preset

Answer

Wires a TypeScript transform so tests import .ts sources directly.

Question

jest.mock hoisting

Answer

Factory calls are lifted above imports so the module registry is patched first.

Question

jest.mocked

Answer

Type-preserving wrapper giving mockResolvedValue/mock.calls the real signatures.

Question

moduleNameMapper

Answer

Jest's equivalent of tsconfig paths — required or alias imports fail at runtime.

Revision Notes

Key Takeaways

  • 1. Test tsconfig stays separate from the app build config
  • 2. Mirror every paths alias in moduleNameMapper
  • 3. Pick one typing source: @types/jest or @jest/globals imports

Interview Tips

  • • Explain hoisting with the module-evaluation-order argument, not "Jest does it"
  • • Describe spy versus module mock and when each is enough

Cheat Sheet

preset ts-jest + dedicated test tsconfig; moduleNameMapper mirrors paths; jest.mocked for typed mocks; restoreMocks in afterEach; ESM needs --experimental-vm-modules.

Continue Learning