Búsqueda de sitios web

typescript - Agregar vitest.d.env para matchers personalizados rompe el soporte de TS de vitest


Escribí un comparador personalizado con expect.extend

expect.extend({
  toEqualIgnoringWhitespace(got: string, want: string) {
    return {
      pass: got.trim().replace(/\s+/g, ``) === want.trim().replace(/\s+/g, ``),
      message: () => `Even with whitespace removed, ${got} does not equal ${want}.`
    };
  }
});

Y como recomiendan los documentos, añadí un vitest.d.env

interface CustomMatchers<R = unknown> {
  toEqualIgnoringWhitespace: () => R
}

declare module 'vitest' {
  type Assertion<T = any> = CustomMatchers<T>
  interface AsymmetricMatchersContaining extends CustomMatchers {}
}

Y seguí los documentos:

No olvides incluir el archivo de declaración ambiental en tu tsconfig.json.

  "include": ["vitest.d.ts","env.d.ts", "**/*.ts", "**/*.tsx"],

Pero ahora TypeScript no sabe sobre el resto de vitest. Usar test.extend, use y task implícitamente tiene tipo any.

const it = test.extend<{ row: EventRow }>({
  row: async ({ task }, use) => {  // errors here
    ...

Se volvió más claro cuando intenté importar cosas de vitest y TS no reconocía las cosas normales como expect (estoy usando globals para describe, it, etc.; probé estas importaciones como una prueba). Solo sugiere las dos cosas del .d.env que pegué de la documentación.

Así que parece que estoy haciendo algo mal en el tsconfig o en el archivo .d.env, pero no sé cómo arreglarlo.

Respuesta #1

Tuve exactamente el mismo problema que tú, sin embargo logré que funcionara incluyendo la palabra clave import o export para que TypeScript sepa que esto es una ampliación de módulo en lugar de un archivo de declaración global gracias a esta respuesta.

vitest.d.ts
import type { Assertion, AsymmetricMatchersContaining } from 'vitest' // This line is important even if imported values are not used!

interface CustomMatchers<R = unknown> {
  toBeFoo: () => R
}

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

Dado que eslint se queja de que los archivos importados no se usaron, lo soluciono usando una exportación vacía explícita.

vitest.d.ts
export {} // Eslint don't complain, and everything works as expected, nice!

interface CustomMatchers<R = unknown> {
  toBeFoo: () => R
}

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

Compartiendo la respuesta completa por si acaso:

vitest.d.ts
export {}

interface CustomMatchers<R = unknown> {
  toBeFoo: () => R
}

declare module 'vitest' {
  interface Assertion<T = any> extends CustomMatchers<T> {}
  interface AsymmetricMatchersContaining extends CustomMatchers {}
}
vitest/custom-matchers.ts
import { expect } from 'vitest'

expect.extend({
  toBeFoo(received, expected) {
    const { isNot } = this
    return {
      // do not alter your "pass" based on isNot. Vitest does it for you
      pass: received === 'foo',
      message: () => `${received} is${isNot ? ' not' : ''} foo`
    }
  }
})
vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    setupFiles: ['./vitest/custom-matchers.ts'],
  },
})
tsconfig.json
{
  // ...
  "include": ["...", "vitest.d.ts"]
}

Respuesta #2

Para mí, me ayudó simplemente renombrar el archivo vitest.d.ts a, por ejemplo, custom-vitest.d.ts

El problema era que TypeScript resolvía "vitest" a "vitest.d.ts", de modo que el módulo declarado en el archivo d.ts estaba sobrescribiendo el módulo predeterminado de vitest en lugar de solo extenderlo. Renombrar el archivo resolvió esto.

También recuerda añadir el archivo "custom-vitest.d.ts" al arreglo "include" en tu tsconfig.json

Artículos relacionados