跳到內容

測試類型

範例專案

GitHub - 線上試玩

Vitest 允許您使用 expectTypeOfassertType 語法為您的類型撰寫測試。預設情況下,*.test-d.ts 檔案中的所有測試都被視為類型測試,但您可以使用 typecheck.include 設定選項來變更它。

在幕後,Vitest 會根據您的設定呼叫 tscvue-tsc,並解析結果。如果 Vitest 發現任何類型錯誤,它也會列印出您原始碼中的類型錯誤。您可以使用 typecheck.ignoreSourceErrors 設定選項來停用它。

請記住,Vitest 不会執行或編譯這些檔案,它們只會由編譯器進行靜態分析,因此您無法使用任何動態陳述。這表示您不能使用動態測試名稱,以及 test.eachtest.runIftest.skipIftest.concurrent API。但您可以使用其他 API,例如 testdescribe.only.skip.todo

對於類型檢查,也支援使用 CLI 旗標,例如 --allowOnly-t

ts
import { assertType, expectTypeOf } from 'vitest'
import { mount } from './mount.js'

test('my types work properly', () => {
  expectTypeOf(mount).toBeFunction()
  expectTypeOf(mount).parameter(0).toMatchTypeOf<{ name: string }>()

  // @ts-expect-error name is a string
  assertType(mount({ name: 42 }))
})

在測試檔案中觸發的任何類型錯誤都將被視為測試錯誤,因此您可以使用任何您想要的類型技巧來測試專案的類型。

您可以在 API 區段 中看到可能的比對器清單。

讀取錯誤

如果您正在使用 expectTypeOf API,請參閱 expect-type 關於其錯誤訊息的文件

當類型不匹配時,.toEqualTypeOf.toMatchTypeOf 使用特殊的輔助類型來產生盡可能可行的錯誤訊息。但了解它們有一些細微差別。由於斷言是「流暢」編寫的,因此失敗應出現在「預期」類型上,而不是「實際」類型上(expect<Actual>().toEqualTypeOf<Expected>())。這表示類型錯誤可能會有點令人困惑 - 因此這個函式庫會產生一個 MismatchInfo 類型來嘗試明確說明預期是什麼。例如

ts
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>()

是一個會失敗的斷言,因為 {a: 1} 的類型是 {a: number} 而不是 {a: string}。在這種情況下的錯誤訊息會讀取類似這樣的內容

test/test.ts:999:999 - error TS2344: Type '{ a: string; }' does not satisfy the constraint '{ a: \\"Expected: string, Actual: number\\"; }'.
  Types of property 'a' are incompatible.
    Type 'string' is not assignable to type '\\"Expected: string, Actual: number\\"'.

999 expectTypeOf({a: 1}).toEqualTypeOf<{a: string}>()

請注意,所報告的類型約束是指定「預期」和「實際」類型的人類可讀訊息。不要逐字理解句子 屬性 'a' 的類型不相容 // 類型 'string' 無法指定給類型 "預期:字串,實際:數字" - 只需查看屬性名稱('a')和訊息:預期:字串,實際:數字。在大多數情況下,這將告訴您出了什麼問題。當然,極其複雜的類型需要更多的除錯工作,並且可能需要一些實驗。如果錯誤訊息實際上具有誤導性,請 提出問題

當測試中的 Actual 類型與預期不符時,toBe... 方法(例如 toBeStringtoBeNumbertoBeVoid 等)會失敗,並解析為不可呼叫的類型。例如,類似 expectTypeOf(1).toBeString() 斷言的失敗訊息會如下所示

test/test.ts:999:999 - error TS2349: This expression is not callable.
  Type 'ExpectString<number>' has no call signatures.

999 expectTypeOf(1).toBeString()
                    ~~~~~~~~~~

This expression is not callable 部分並無多大幫助,有意義的錯誤訊息在下一行,Type 'ExpectString<number> has no call signatures。這基本上表示你傳入了一個數字,但斷言它應該是字串。

如果 TypeScript 新增了對 "throw" 類型 的支援,這些錯誤訊息可以大幅改善。在那之前,它們需要仔細閱讀。

具體的「預期」物件與類型參數

類似這樣的斷言的錯誤訊息

ts
expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' })

會比類似這樣的斷言的錯誤訊息沒那麼有幫助

ts
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>()

這是因為 TypeScript 編譯器需要推斷 .toEqualTypeOf({a: ''}) 樣式的類型參數,而這個函式庫只能透過與泛型 Mismatch 類型進行比較來標記它為失敗。因此,如果可能的話,請對 .toEqualTypeOftoMatchTypeOf 使用類型參數,而不是具體類型。如果比較兩個具體類型更方便,你可以使用 typeof

ts
const one = valueFromFunctionOne({ some: { complex: inputs } })
const two = valueFromFunctionTwo({ some: { other: inputs } })

expectTypeOf(one).toEqualTypeof<typeof two>()

如果你發現使用 expectTypeOf API 和找出錯誤很困難,你可以隨時使用更簡單的 assertType API

ts
const answer = 42

assertType<number>(answer)
// @ts-expect-error answer is not a string
assertType<string>(answer)

提示

使用 @ts-expect-error 語法時,你可能想要確保自己沒有打錯字。你可以透過在 test.include 設定選項中包含你的類型檔案來做到這一點,這樣 Vitest 實際上也會執行這些測試,並以 ReferenceError 失敗。

這將會通過,因為它預期會發生錯誤,但「answer」這個字打錯了,所以這是一個誤判錯誤

ts
// @ts-expect-error answer is not a string
assertType<string>(answr) //

執行類型檢查

自 Vitest 1.0 以後,若要啟用類型檢查,只需在 package.json 中將 --typecheck 標記新增到你的 Vitest 指令即可

json
{
  "scripts": {
    "test": "vitest --typecheck"
  }
}

現在您可以執行類型檢查

bash
npm run test
bash
yarn test
bash
pnpm run test
bash
bun test

Vitest 使用 tsc --noEmitvue-tsc --noEmit,具體取決於您的組態,因此您可以從您的管線中移除這些指令碼。