Exception matchers such as toThrow must receive a synchronous callback. The callback is invoked by the matcher so that it can observe
an exception thrown by the operation. This rule applies to the expect APIs from Jest, Vitest, and bun:test.
The toThrow matcher checks whether a function throws an exception when it is invoked. Passing the result of calling an operation
evaluates that operation before toThrow runs. If the operation throws, the exception propagates immediately. Otherwise, the matcher
receives a non-function value and raises a matcher-usage error instead of checking the expected exception. Pass a callback that performs the operation
instead.
An async function is callable, but invoking it always returns a promise. Exceptions raised in its body become promise rejections, so a
synchronous toThrow matcher cannot observe them. Use the rejects modifier with the promise returned by the async operation
instead.
The rule reports only when type information proves that the received value is not callable or when the callback is explicitly async. Values with
the types any, unknown, or an otherwise uncertain union are ignored.
import { expect } from 'vitest';
function parseConfig(input: string): object {
return JSON.parse(input);
}
expect(parseConfig(']')).toThrow(SyntaxError); // Noncompliant: "toThrow" must receive a function, not the result of calling "parseConfig"
import { expect } from 'vitest';
function parseConfig(input: string): object {
return JSON.parse(input);
}
expect(() => parseConfig(']')).toThrow(SyntaxError);
import { expect } from 'bun:test';
expect(async () => { // Noncompliant: a synchronous "toThrow" matcher cannot observe a rejection from an async function
throw new Error('failure');
}).toThrow('failure');
import { expect } from 'bun:test';
async function loadUser() {
throw new Error('failure');
}
await expect(loadUser()).rejects.toThrow('failure');
toThrowtoThrow