WillD
WillD

Reputation: 6552

Vitest defineConfig, 'test' does not exist in type 'UserConfigExport'

Trying to setup vitest on an already existing vite (vue 3, typescript) project.

My vite.config.ts looks like this:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
  },
  plugins: [vue()],
});

But in VS code it complains:

vscode red lines under test prop

On hover I see:

Argument of type '{ test: { globals: boolean; environment: string; }; plugins: Plugin[]; }' is not assignable to parameter of type 'UserConfigExport'. Object literal may only specify known properties, and 'test' does not exist in type 'UserConfigExport'.ts(2345)

I can make it go away if I change this line:

import { defineConfig } from 'vite';

To:

import { defineConfig } from 'vitest/config';

But why? What's up with this? Why should I have to import defineConfig from vitest in order to get it to support the test property?

Upvotes: 136

Views: 73288

Answers (20)

alexdsgmoura
alexdsgmoura

Reputation: 11

My solution

vite.config.ts

import { defineConfig } from "vitest/config"
import tsConfigPaths from "vitest-tsconfig-paths"

export default defineConfig({
  plugins: [tsConfigPaths()],
  test: {
    globals: true,
  },
})

update your file tsconfig.json inserting following lines below.

tsconfig.json

{
  "baseUrl": "./",
  "paths": {
    "@/*": ["./src/*"]
  },
  "types": ["vitest/globals"]
}

Upvotes: 1

Mbiplang Ardel
Mbiplang Ardel

Reputation: 700

Short Answer:

If you're encountering type errors when using vite and vitest in your vite.config.ts file, it's likely due to incompatible types between the defineConfig functions from vite and vitest/config.

Here's how I resolved the issue:

import { defineConfig as testConfig } from "vitest/config";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// Vite configuration
const config = defineConfig({
  plugins: [vue()],
});

// Vitest configuration
const tstConfig = testConfig({
  test: {
    environment: "jsdom",
  },
});

// Merge configurations
export default {
  ...config,
  ...tstConfig,
};

  • Separate Configurations: By defining config and tstConfig separately and then merging them, you avoid type conflicts between vite and vitest.

This approach should resolve the type errors and allow you to use both vite and vitest configurations.

Upvotes: 2

Crystal A
Crystal A

Reputation: 1

As someone mentioned, what worked for me was to install compatible versions of vite and vitest. When I typed the npm ls vite command, I realized there were different versions of vite, so I installed the specific version that vitest installed and I no longer get the error. If this is not a good solution, please let me know.

Upvotes: 0

ronen
ronen

Reputation: 2598

Here's a more minimal approach than existing answers (using typescript 5.7.2, vite 6.0.3, and vitest 2.1.8/3.0.0-beta.2):

import type { UserConfig } from 'vite'
import type { UserConfig as VitestConfig } from 'vitest/node'

export default {
    // ...vite config...
    test: {
        // ...vitest config...
    }
} as UserConfig & { test: VitestConfig }

(Note: vite's UserConfig is documented, but vitest's UserConfig doesn't seem to be documented -- so possibly this may not continue to work in later releases of vitest)

Upvotes: 0

Michal Levý
Michal Levý

Reputation: 37903

Short answer:

Because this is how TypeScript works. Vite config interface does not know anything about Vitest and TS does not allow excessive properties (properties not defined by the type/interface)

So Vitest must extend Vite config (defined as TS interface). In order to use this extended interface instead of the original one, you must first import it.

Instead of doing this (importing pure Vite config):

import { defineConfig } from 'vite';

do this (importing Vite config extended by Vitest):

import { defineConfig } from 'vitest/config';

Alternatively you can also use a TS triple slash command as documented in Managing Vitest config file

/// <reference types="vitest/config" />
import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    // ...
  },
})

If you are using Vitest version older than 2.1:

/// <reference types="vitest" />

Upvotes: 179

ThdK
ThdK

Reputation: 10576

You have to update your tsconfig.json file so that it:

  1. includes the vite.config.ts file
  2. included types from vite even if not imported in any file

tsconfig.json

{
   "compilerOptions": {
      "types": [
         "vitest"
     ],  
  },
  "include": [
    "src",
    "vite.config.ts"
  ]
}

vite.config.ts

import { defineConfig } from 'vite';

export default defineConfig({
  ...
  test: {
     ...
  },
});

Upvotes: 1

David Martin
David Martin

Reputation: 1

This works for me and it's clean imo.

import { defineConfig } from 'vite';
import type { UserConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

const config: UserConfig = {
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
  },
};

// https://vitejs.dev/config/
export default defineConfig(config);

Upvotes: 0

Augustine Calvino
Augustine Calvino

Reputation: 301

TLDR

Assert your config object's type as UserConfig (see last line):

import { defineConfig, UserConfig } from 'vite'

export default defineConfig({
    // your vite config
    test: {
        environment: 'jsdom',
        setupFiles: 'src/setup-tests.tsx',
        coverage: {...},
    },
} as UserConfig)

Explaination

The other answers here either did not work, or were to complex for me to want to even try.

Eventually, after extending UserConfig in a few variations of this:

declare module 'vite' { interface UserConfig... }

I realized that the error wasn't complaining about UserConfig, but UserConfigExport:

Object literal may only specify known properties, and test does not exist in type 'UserConfigExport'

So that's why it wasn't taking - defineConfig is overloaded with 4 signatures, and typescript for some reason was matching my invocation to the fourth option shown here, rather than the first.

declare function defineConfig(config: UserConfig): UserConfig;
declare function defineConfig(config: Promise<UserConfig>): Promise<UserConfig>;
declare function defineConfig(config: UserConfigFnObject): UserConfigFnObject;
declare function defineConfig(config: UserConfigExport): UserConfigExport;

That is, it didn't matter how I extended UserConfig through a global module definition, or how or where I included the triple-slash reference directive suggested in the docs (which as far as I understand it basically does the same thing), because my config object wasn't being typed as a UserConfig at all!

Once I realized that, I tried the type assertion and all was well. No custom module declarations, not triple-slash reference at the top of the file - just the type assertion.

Upvotes: 1

Eduard Jacko
Eduard Jacko

Reputation: 2151

as 2024 you can just import vitest/config which pulls the extended types automatically

import {defineConfig} from "vite";
import "vitest/config" // <-- just dummy import

export default defineConfig({
  test: {
    // your stuff here
  }
})

Upvotes: 7

kthompson
kthompson

Reputation: 892

I had a similar issue that was resolved by using a version of vitest and vite that were compatible. In my case the latest version of each. I assume using versions that were released around the same time frame would work as well.

yarn install -D vitest vite

Upvotes: 7

Usama
Usama

Reputation: 529

Replacing the function with an arrow function worked for me. Mentioned at https://github.com/vitest-dev/vitest/discussions/1106#discussioncomment-2531279

export default defineConfig(() => ({
  build: {
    // ...
  },
  test: {
    // ...
  },
}));

Upvotes: 5

pkirilin
pkirilin

Reputation: 500

None of the options listed above helped me. Eventually, I've managed to solve this issue using the type augmentation feature:

import type { UserConfig as VitestUserConfig } from 'vitest/config';
import { defineConfig } from 'vite';

declare module 'vite' {
  export interface UserConfig {
    test: VitestUserConfig['test'];
  }
}

export default defineConfig({
  // add your vite configuration here

  test: {
    // add your vitest configuration here
  },
});

Upvotes: 3

Sanka Sanjeeva
Sanka Sanjeeva

Reputation: 3550

We can use mergeConfig method from vite or vitest/config entries to merge Vite config with Vitest config:

import { defineConfig as defineViteConfig, mergeConfig } from 'vite';
import { defineConfig as defineVitestConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

const viteConfig = defineViteConfig({
  plugins: [react()],
});

const vitestConfig = defineVitestConfig({
  test: {
    // ...
  },
});

export default mergeConfig(viteConfig, vitestConfig);

Upvotes: 20

power Eich
power Eich

Reputation: 11

The above method doesn't work for me, then I try this, It work.

/// <reference types="vitest" />
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx'; // 支持jsx语法

// https://vitejs.dev/config/
export default defineConfig(() => {
  return {
    plugins: [vue(), vueJsx()],
    test: {
      globals: true,
      environment: 'jsdom',
      transformMode: { web: [/.[tj]sx$/] },
    },
  };
});

Upvotes: 1

lajtmaN
lajtmaN

Reputation: 465

It might be because you have multiple versions of vite installed. Search in your package-lock or yarn-lock file to find the entries for vite, and make sure there's only one of them. After that, I got it to work using the triple-slash.

Upvotes: 0

Nazmul Hasan
Nazmul Hasan

Reputation: 95

I had a similar issue with my project, but after adding /// <reference types="vitest" /> this line at the top of the file, the error is gone.

/// <reference types="vitest" />
import { defineConfig } from 'vite';

export default defineConfig({
  ...
  test: {
   ...
  },
});

Upvotes: -2

Eugene Snihovsky
Eugene Snihovsky

Reputation: 630

sunny prakash solution is quite good, but it allow wrong type in config due to as VitestConfigExport construction.

Better way is to move config to separate constant and use it

import type { InlineConfig } from 'vitest';
import type { UserConfig } from 'vite';

type ViteConfig = UserConfig & { test: InlineConfig };
const config: ViteConfig = {
  // other config
  test: {
    environment: 'jsdom',
  },
};
export default defineConfig(config);

Upvotes: 7

Jonathan Orrego
Jonathan Orrego

Reputation: 341

I separated the files because i got the from this question and if i changed the import to vitest i got another error in the plugin react line.

  • vite.config.ts
  • vitest.config.ts

vitest:

import { defineConfig } from 'vitest/config';

    export default defineConfig({
        test: {
          globals: true,
          environment: 'jsdom'
        },
      })

vite:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
  },
})

Upvotes: 26

Sunny Prakash
Sunny Prakash

Reputation: 870

Here is what I did in case someone is still looking for a solution. I created my own type and extended to UserConfig.

...
import type { InlineConfig } from 'vitest';
import type { UserConfig } from 'vite';

interface VitestConfigExport extends UserConfig {
  test: InlineConfig;
}
...

Then I casted the type of config object to my custom interface -

export default defineConfig({
  plugins: [solidPlugin()],
  server: {
    port: 3000,
  },
  test: {
    environment: 'jsdom',
    globals: true,
    transformMode: {
      web: [/\.[jt]sx?$/],
    },
    setupFiles: './setupVitest.ts',
  },
  build: {
    target: 'esnext',
  },
} as VitestConfigExport);

This also enabled intellisense for new test property. Also, you don't need to declare /// <reference types="vitest" />. It might help.

Upvotes: 23

benjaroa
benjaroa

Reputation: 367

I was in a similar situation, asking me the same thing and end up doing this:

/// <reference types="vitest" />
import { defineConfig } from 'vite';
import type { UserConfig as VitestUserConfigInterface } from 'vitest/config';

const vitestConfig: VitestUserConfigInterface = {
  test: {
    // vitest config, with helpful vitest typing :)
  }
};

export default defineConfig({
  test: vitestConfig.test,
  // and now: just vite config
});

It may be helpful.

Upvotes: 16

Related Questions