Skip to Content
Course content

217: Configuring Vitest with TypeScript

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Alright, let's get Vitest wired up to our TypeScript project. I've already got a small utility library here—a CartCalculator that handles taxes and discounts—and I want to make sure the math is solid before I ship it. You'd think since Vitest is built on Vite, it would just "work" with TypeScript, and for the most part, it does. But if we just dive in without a plan, the developer experience is usually a bit clunky.

// src/cart.ts
export interface Item {
  price: number;
  quantity: number;
}

export function calculateTotal(items: Item[], taxRate: number): number {
  const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  return subtotal + subtotal * taxRate;
}

Just throwing it at the wall

I'll start by installing Vitest and writing a quick test. I'm not going to touch any config files yet; I just want to see if the runner can pick up the TS files. I'll create src/cart.test.ts:

// src/cart.test.ts
import { describe, it, expect } from 'vitest';
import { calculateTotal } from './cart';

describe('calculateTotal', () => {
  it('should apply tax correctly', () => {
    const items = [{ price: 10, quantity: 2 }];
    expect(calculateTotal(items, 0.1)).toBe(22);
  });
});

I run npx vitest. It works! The test passes. But look at my editor. I'm seeing red squiggles under describe and it. Now, I explicitly imported them from 'vitest', so why is TypeScript complaining? Actually, in this specific case, it isn't—but if I were to remove those imports to keep the test file cleaner, the whole thing falls apart. Most of us prefer the "global" style for tests so we aren't importing the same five functions in every single file.

The "Cannot find name 'describe'" headache

Let's try to use globals. I'll remove the import { describe, it, expect } from 'vitest' line. Immediately, TypeScript screams at me: Cannot find name 'describe'. Vitest provides these globals, but TypeScript has no idea they exist in the global namespace. It doesn't just "know" because the library is installed.

I could just ignore it, but that defeats the purpose of using TypeScript. I'll head over to tsconfig.json. I need to tell the compiler that Vitest's types should be available everywhere.

// tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "node",
    "types": ["vitest/globals"] // Add this line
  }
}

The squiggles vanish. Great. But wait—when I run npx vitest again, the tests fail. Why? Because I told TypeScript that the globals exist, but I haven't told Vitest to actually provide them at runtime. I've basically lied to the compiler.

Making it official with a config file

This is where we actually need a configuration file. Vitest looks for vitest.config.ts (or vite.config.ts). I'll create one in the root. I need to enable the globals flag so that describe and it are actually injected into the environment.

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    // I'll add this while I'm here—it helps with debugging
    environment: 'node', 
  },
});

Now, when I run the tests, they pass, and my editor is happy. I've synced the runtime (the config file) with the type system (the tsconfig).

A quick note on the "Vite" overlap

You might notice I imported defineConfig from 'vitest/config' instead of just 'vite'. If you're building a frontend app with Vite, you can actually put your test config right inside your vite.config.ts. But for a library or a backend project, keeping it separate in vitest.config.ts is much cleaner. It prevents your production build config from being cluttered with test-only settings.

So, the workflow is: Install → Config file (runtime) → tsconfig (types). If you miss one, you're either fighting your editor or fighting the test runner.




📋 Practical Task

Fixing the UserPermission Guard Test Setup

You've been handed a project with a UserPermission guard that checks if a user has a specific role. The code is written in TypeScript, and Vitest is installed, but the project is in a "broken" state: the tests are failing to run because of missing globals, and the IDE is full of TypeScript errors regarding the test functions.

The Code:

// src/permissions.ts
export function hasPermission(userRole: string, requiredRole: string): boolean {
  return userRole === requiredRole || userRole === 'admin';
}

// src/permissions.test.ts
describe('hasPermission', () => {
  it('should return true if user is admin', () => {
    expect(hasPermission('admin', 'editor')).toBe(true);
  });

  it('should return false if roles do not match', () => {
    expect(hasPermission('viewer', 'editor')).toBe(false);
  });
});

Your Task:

  1. Create a vitest.config.ts file that enables global test functions.
  2. Update the tsconfig.json to ensure TypeScript recognizes the Vitest globals, eliminating the "Cannot find name" errors.
  3. Ensure the tests can be executed via npx vitest without needing to manually import describe, it, or expect in the test file.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.