I’m trying to separate my TypeScript test configs for a Vite + React + Vitest project. The tsconfig.app.json
and tsconfig.node.json
files were created automatically by Vite; I only added my own tsconfig.test.json
.
I now have:
tsconfig.json (references the others)
tsconfig.app.json
tsconfig.node.json
tsconfig.test.json
The problem is: ESLint does not recognize Vitest globals (describe, it, expect, etc.) in my test files. TypeScript compiles fine when running Vitest, but ESLint shows the error:
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.ts(2582)
Even though I already added "types": ["vitest/globals"]
in my tsconfig.test.json.
My setup
tsconfig.test.json
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"types": ["vitest/globals"]
},
"include": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/tests/setup.ts"]
}
tsconfig.json
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.test.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
vitest.config.ts
import { resolve } from 'path';
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/tests/setup.ts',
css: true,
typecheck: {
tsconfig: './tsconfig.test.json',
},
},
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
});
eslint.config.ts
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
import reactX from 'eslint-plugin-react-x';
import reactDom from 'eslint-plugin-react-dom';
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [
js.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
...tseslint.configs.stylisticTypeChecked,
],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
'react-x': reactX,
'react-dom': reactDom,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
...reactX.configs['recommended-typescript'].rules,
...reactDom.configs.recommended.rules,
},
},
// Test files configuration
{
files: ['src/**/*.test.{ts,tsx}', 'src/tests/setup.ts'],
languageOptions: {
parserOptions: {
project: './tsconfig.test.json',
},
},
},
{
files: ['cypress/**/*.{ts,tsx}'],
extends: ['plugin:cypress/recommended'],
}
);
What I tried:
"types": ["vitest/globals"]
in tsconfig.app.json → ESLint stops complaining. But I don’t want my main app config polluted with test-only types.How do I correctly configure ESLint + TypeScript so it recognizes Vitest globals (describe, it, etc.) from my tsconfig.test.json only without needing to add them into tsconfig.app.json?
Issue
tsconfig.app.json
included the whole src
folder, which also contained test files. This meant the tests were type-checked by both tsconfig.app.json
and tsconfig.test.json
, which caused conflicts and ESLint didn’t recognize Vitest globals.
Fix
Exclude test files from tsconfig.app.json
so only tsconfig.test.json
handles them.
tsconfig.app.json
{
// Vite defaults...
"exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/tests/setup.ts"]
}
tsconfig.test.json
{
"compilerOptions": {
"types": ["vitest/globals"],
"lib": ["ES2020", "DOM"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx"
},
"include": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/tests/setup.ts"]
}
After this change, ESLint recognized describe
, it
, expect
, etc.
(Optional): I also added @vitest/eslint-plugin
to my ESLint config. Not required for fixing the globals error, but helpful for extra rules and best practices in tests.