Skip to content

Vitest 5: matcher types are lost — Assertion<T> augmentation no longer merges with Vitest's Assertion<R, T> #738

Description

@meseven
  • @testing-library/jest-dom version: 7.0.1
  • node version: 24.19.0
  • jest (or vitest) version: vitest 5.0.0
  • npm (or yarn) version: pnpm 11.21.0
  • typescript version: 7.0.2 (same result with 5.9)

Relevant code or config:

types/vitest.d.ts as shipped in 7.0.1 (and on main):

import 'vitest'
import {type TestingLibraryMatchers} from './matchers'

declare module 'vitest' {
  interface Assertion<T = any>
    extends TestingLibraryMatchers<any, T> {}
  interface AsymmetricMatchersContaining
    extends TestingLibraryMatchers<any, any> {}
}

Vitest 5.0.0 changed the interface this augments. Assertion now takes the return type first and the received type second (vitest/dist/chunks/config.d.*.d.ts):

interface Assertion<
  R extends void | Promise<void> = void,
  T = unknown
> extends VitestAssertion<Chai.Assertion, R, T>, JestAssertion<R, T>, ChaiMockAssertion<R, T>, Matchers<R, T> {  }

What you did:

Upgraded a Vitest 4.1.11 project (vitest, @vitest/ui, @vitest/coverage-v8) to 5.0.0, with import '@testing-library/jest-dom/vitest' in the setup file, and ran tsc.

What happened:

Every jest-dom matcher disappears from the assertion type. 1,203 errors of the form:

src/…/status-cell.test.tsx(53,38): error TS2339: Property 'toBeInTheDocument' does not exist on type 'Assertion<void, HTMLElement>'.
src/…/status-cell.test.tsx(116,7): error TS2339: Property 'toBeDisabled' does not exist on type 'Assertion<void, HTMLElement>'.

The runtime side is fine — expect.extend still registers the matchers and the tests pass. Only the types are gone.

Cause: TypeScript only merges interface declarations whose type parameter lists are identical. jest-dom's Assertion<T = any> (one parameter) no longer matches Vitest's Assertion<R, T> (two), so the augmentation is dropped. The mismatch itself is reported inside node_modules (TS2428 "All declarations of 'Assertion' must have identical type parameters"), which skipLibCheck: true hides — so the only visible symptom is the matchers vanishing.

Reproduction:

mkdir repro && cd repro && npm init -y
npm i -D vitest@5.0.0 typescript @testing-library/jest-dom@7.0.1 @testing-library/dom
cat > a.test.ts <<'TS'
import '@testing-library/jest-dom/vitest'
import { expect, it } from 'vitest'
it('x', () => { expect(document.body).toBeInTheDocument() })
TS
npx tsc --noEmit --skipLibCheck --moduleResolution bundler --module esnext --target es2022 --lib es2022,dom a.test.ts
# a.test.ts(3,38): error TS2339: Property 'toBeInTheDocument' does not exist on type 'Assertion<void, HTMLElement>'.

With vitest@4.1.11 the same file type-checks.

Problem description:

@testing-library/jest-dom/vitest is the documented way to get the matchers typed under Vitest, and it silently stops working on Vitest 5 — no error at the import site, no runtime failure, just every toBeInTheDocument() failing tsc. Vitest 5.0.0 was released 2026-09-03; the migration guide lists the Assertion<R, T> change under "Assertion Type Parameters".

Suggested solution:

Extend Matchers<R, T>, which is the extension point Vitest 5 documents for custom matchers, instead of Assertion. TestingLibraryMatchers<E, R> already takes the return type as its second parameter, so it slots in directly:

import 'vitest'
import type { ExpectStatic } from 'vitest'
import {type TestingLibraryMatchers} from './matchers'

type AsymmetricMatcher = ReturnType<ExpectStatic['stringContaining']>

declare module 'vitest' {
  interface Matchers<R, T>
    extends TestingLibraryMatchers<AsymmetricMatcher, R> {}
  interface AsymmetricMatchersContaining
    extends TestingLibraryMatchers<AsymmetricMatcher, any> {}
}

This is what we ship locally as an ambient jest-dom.d.ts to keep the upgrade green, and it restores all matchers with the correct return type (void for sync assertions, Promise<void> under resolves/rejects).

Caveat: Matchers in Vitest ≤ 4 has a single type parameter, so this declaration is Vitest 5-only; the current Assertion<T> one is ≤ 4-only. If the package wants to keep supporting both, that probably means a version-gated entry point (@testing-library/jest-dom/vitest for ≥ 5 and the old declaration under another name, or the reverse). Happy to open a PR either way if you tell me which shape you prefer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions