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:
- Running vitest → works fine, globals are recognized.
- Adding
"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?