Test TypeScript Code with Vitest
Set up Vitest to test TypeScript code with zero configuration. Write unit tests, run them in watch mode, and use built-in type testing to catch type-level bugs.
Vitest is a test runner built on top of Vite. It runs your test files, reports which pass and which fail, and re-runs tests automatically when you change your code. Because it uses esbuild for transpilation, it understands TypeScript natively without extra plugins or configuration.
You write tests in files named with the .test.ts or .spec.ts pattern. Vitest finds them, transforms the TypeScript to JavaScript on the fly, and executes them. For choosing the right TypeScript executor for your dev workflow, see tsx vs ts-node for Running TypeScript.
Install and Write Your First Test
Install Vitest as a dev dependency:
npm install --save-dev vitestWith Vitest installed, create the code you want to test. Write a simple utility function in a file called src/math.ts:
export function add(a: number, b: number): number {
return a + b;
}Now create a test file alongside it. The test and expect functions come directly from vitest. Each test calls the add function with different inputs and checks the result:
import { expect, test } from "vitest";
import { add } from "./math";
test("adds two positive numbers", () => {
expect(add(2, 3)).toBe(5);
});
test("adds a positive and a negative number", () => {
expect(add(10, -4)).toBe(6);
});Add a test script to your package.json so you can run tests with a single command. This wires vitest into your npm workflow and lets every contributor run the same test suite:
{
"scripts": {
"test": "vitest"
}
}Now execute the tests from your terminal. Vitest scans your project for test files, transforms the TypeScript on the fly, and reports each individual test result with a clear pass or fail status next to the test name:
npm testVitest prints this to your terminal after the run finishes. Each test file gets its own line, and each test case underneath it gets a green checkmark for a pass or a red mark for a failure:
✓ src/math.test.ts (2)
✓ adds two positive numbers
✓ adds a positive and a negative number
Test Files 1 passed (1)
Tests 2 passed (2)
Duration 105msVitest found the test file, transformed the TypeScript, and ran both tests. No config file was needed.
Watch Mode
Run vitest without the run subcommand and it stays in watch mode, waiting for file changes:
npx vitestIn watch mode, Vitest re-runs only the tests affected by your changes. If you edit src/math.ts, only math.test.ts runs again. This keeps the feedback loop under a second, even in projects with hundreds of test files.
Common Matchers
Vitest's expect API is compatible with Jest for most matchers. Here are the most useful ones grouped by category.
For equality and truthiness checks:
import { expect, test } from "vitest";
test("equality and truthiness", () => {
expect(2 + 2).toBe(4);
expect({ name: "Ada" }).toEqual({ name: "Ada" });
expect("hello").toBeTruthy();
expect(null).toBeNull();
});Numbers need approximate matching due to floating point, and you can check ordering with matchers like toBeGreaterThan. Strings support substring checks with toContain:
test("numbers and strings", () => {
expect(0.1 + 0.2).toBeCloseTo(0.3);
expect(5).toBeGreaterThan(2);
expect("TypeScript").toContain("Script");
});Arrays can be checked for length and content, and you can verify that functions throw specific errors. Use toThrow to confirm both the error type and message:
test("arrays and errors", () => {
expect([1, 2, 3]).toHaveLength(3);
expect(() => {
throw new Error("broken");
}).toThrow("broken");
});Each matcher checks one specific condition. If the condition is not met, Vitest prints the expected and received values so you can see exactly what went wrong.
Testing Async Code
Vitest handles async tests the same way for both promises and async/await patterns:
import { expect, test } from "vitest";
async function fetchUser(id: number): Promise<{ id: number; name: string }> {
return { id, name: "Ada" };
}
test("resolves with a user using async/await", async () => {
const user = await fetchUser(1);
expect(user).toEqual({ id: 1, name: "Ada" });
});The resolves matcher waits for the promise to settle before checking the value. The rejects matcher does the same for expected failures. This lets you test both success and error paths cleanly.
Grouping Tests with describe
Group related tests with describe blocks to keep your test output organized:
import { describe, expect, test } from "vitest";
describe("math utilities", () => {
test("adds positive numbers", () => {
expect(2 + 3).toBe(5);
});
test("handles zero", () => {
expect(5 + 0).toBe(5);
});
});Describe blocks can be nested. Vitest prints the full path of each test in the output so you can tell exactly which group a failing test belongs to.
Typecheck Mode
Runtime tests verify behavior. Typecheck mode verifies types. When you run vitest with the --typecheck flag, it uses the TypeScript compiler to check your test files and reports type errors as test failures.
npx vitest run --typecheckUse a dedicated script in your package.json so you can typecheck with a short command. Having both runtime tests and type checks in your npm scripts keeps your workflow consistent:
{
"scripts": {
"test": "vitest",
"test:typecheck": "vitest --typecheck"
}
}With both scripts in place, npm test runs runtime tests and npm run test:typecheck runs type assertions. Use both in CI for full coverage.
Typecheck mode opens the door to compile-time assertions. You can write tests that check whether a function returns the correct type, without ever executing the function. For a complete guide on those patterns, see Write Type Safe Unit Tests.
Rune AI
Key Insights
- Install Vitest with npm install -D vitest and add a test script to package.json.
- Vitest understands TypeScript natively via esbuild; no ts-jest or babel config needed.
- Write tests with test() and expect(), using the same API as Jest for most matchers.
- Use vitest --typecheck to run compile-time type assertions with expectTypeOf.
- Watch mode re-runs only the tests affected by your changes for fast feedback.
Frequently Asked Questions
Does Vitest work without a vite.config.ts?
Can Vitest replace Jest for TypeScript projects?
What is the difference between vitest and vitest --typecheck?
Conclusion
Vitest runs TypeScript tests out of the box with no configuration. Install it, write tests with import { expect, test } from 'vitest', and run them. Add --typecheck to catch type-level bugs that runtime tests cannot see.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.