Recipe · Frameworks and tests

How do I type-check Vitest or Jest test files in JavaScript?

  • Node.js (ES modules)
  • Vitest
  • Jest
  • Test files
  • types
  • Checked with TypeScript 5.9

The problem My tests are .js files. When I include them in jsconfig.json, tsc says it cannot find describe and expect. How do I get the test functions typed, so the tests are checked like the code they test?

The short answer

Import the test functions from vitest in each test file, and make sure jsconfig.json includes the tests:

price.test.js price.test.ts TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 : differs, see why TypeScript 7.0.2, run 21 October 2026: differs from 5.9.3. /project/node_modules/@types/chai/index.d.ts(539,13): error TS2300: Duplicate identifier 'containSubset'. /project/node_modules/vitest/dist/chunks/global.d.MAmajcmJ.d.ts(38,4): error TS2300: Duplicate identifier 'containSubset'. checked 21 October 2026
import { describe, expect, it } from "vitest";
import { formatPrice } from "./price.js";

describe("formatPrice", () => {
  it("formats cents as dollars", () => {
    expect(formatPrice(1999)).toBe("$19.99");
  });

  it("uses the currency it is given", () => {
    expect(formatPrice(500, "EUR")).toBe("€5.00");
  });
});
import { describe, expect, it } from "vitest";
import { formatPrice } from "./price.js";

describe("formatPrice", () => {
  it("formats cents as dollars", () => {
    expect(formatPrice(1999)).toBe("$19.99");
  });

  it("uses the currency it is given", () => {
    expect(formatPrice(500, "EUR")).toBe("€5.00");
  });
});
A test file that imports its test functions from vitest.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": ["node"]
  },
  "include": ["*.js"]
}

describe, it and expect come with their types from the vitest package, and the calls into price.js are checked like any other code: a wrong argument in a test is an error before the test runs.

Why it works

Vitest ships its own type declarations, so an import is enough for tsc to find them; nothing extra goes in types. The test file is a normal module in the program, so everything it calls is checked against the JSDoc of the code under test. That is often where checking pays off first: tests call your functions in many ways.

If you prefer the global style (globals: true in the Vitest config), tell tsc about the globals by listing vitest/globals in types:

price.test.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
import { formatPrice } from "./price.js";

describe("formatPrice", () => {
  it("formats cents as dollars", () => {
    expect(formatPrice(1999)).toBe("$19.99");
  });
});
Global test functions, declared by listing vitest/globals in types.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": ["vitest/globals"]
  },
  "include": ["*.js"]
}

The same thing in TypeScript

The TypeScript equivalent tab of the first example is the same test as a .ts file. It imports ./price.js, the JavaScript module, which a TypeScript project can do with allowJs.

What goes wrong

Globals used without their types are unknown names. With a types list in the config, describe and it are TS2593, whose message suggests installing a test runner’s types, and expect is TS2304. The type error in the call to formatPrice is reported as well:

price.test.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: price.test.js(4,1): error TS2593: Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig. price.test.js(6,3): error TS2593: Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig. price.test.js(8,5): error TS2304: Cannot find name 'expect'. price.test.js(8,24): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. price.test.js(4,1): error TS2593: Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig. price.test.js(6,3): error TS2593: Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig. price.test.js(8,5): error TS2304: Cannot find name 'expect'. price.test.js(8,24): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. price.test.js(4,1): error TS2593: Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig. price.test.js(6,3): error TS2593: Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig. price.test.js(8,5): error TS2304: Cannot find name 'expect'. price.test.js(8,24): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. checked 21 October 2026
import { formatPrice } from "./price.js";

describe("formatPrice", () => {error TS2593: Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.
  it("formats cents as dollars", () => {error TS2593: Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.
    expect(formatPrice("19.99")).toBe("$19.99");error TS2304: Cannot find name 'expect'.error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
  });
});
Globals with no types, and a test that calls the code wrongly.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Under TypeScript 6.0 and 7.0

Every example on this page also runs under TypeScript 6.0.3 and 7.0.2 when this site is built. One of them reports something different from 5.9.3, the version the page describes; the ✗ on its badge links to its entry below.

Conflicts between packages' types are reported under skipLibCheck

TypeScript 7.0 · other checker changes

The pattern: A test file that imports from vitest in a project that also has @types/chai installed (vitest 3.2.7 and @types/chai 5.2.3 both declare containSubset).

Why: The port's list of changes says conflicting declarations now error in every file that contributes to them, so a conflict that skipLibCheck used to hide is reported (TS2300, "Duplicate identifier") in the two packages' type files. Nothing in the example's own files changes. Source: CHANGES.md: Error reporting under --skipLibCheck.

A test file that imports its test functions from vitest price.test.js

TypeScript 5.9.3

No errors.

TypeScript 6.0.3same as 5.9.3

No errors.

TypeScript 7.0.2differs from 5.9.3
/project/node_modules/@types/chai/index.d.ts(539,13): error TS2300: Duplicate identifier 'containSubset'.
/project/node_modules/vitest/dist/chunks/global.d.MAmajcmJ.d.ts(38,4): error TS2300: Duplicate identifier 'containSubset'.

What works in every version: Keep the packages on versions whose declarations agree. In our run, the same packages used through "types": ["vitest/globals"] reported nothing under 7.0.2.

The example Global test functions, declared by listing vitest/globals in types on this page shows it, with the same result under every version.

Gotchas

  • TS2582 or TS2593. Without a types list the same missing globals are TS2582; with one, TS2593. Both mean “these names come from a test runner’s types”.
  • Include the test files. A jsconfig.json whose include lists only src/**/*.js leaves test/ unchecked; add the test folder, or use a pattern that covers it.
  • Matchers are loosely typed. expect(value).toBe(other) accepts any other, so assertions do not catch type mismatches the way calls to your code do.

Questions

What about Jest?

Per the Jest documentation, either import the functions from @jest/globals (import { describe, expect, it } from "@jest/globals") or install @types/jest and add "jest" to types. This page's examples run Vitest only.

Should test files be in the same jsconfig.json as the code?

Usually yes: the tests then check calls into your code with the same settings. If tests need different globals, a second config that extends the first and adds the test types keeps the main config clean.

Sources

Last reviewed 21 October 2026.