型別測試
Vitest 允許您使用 expectTypeOf 或 assertType 語法來編寫型別測試。預設情況下,所有 *.test-d.ts 檔案中的測試都會被視為型別測試,但您可以使用 typecheck.include 配置選項來調整此設定。
在底層,Vitest 會根據您的配置呼叫 tsc 或 vue-tsc,並解析其結果。如果 Vitest 偵測到任何型別錯誤,它也會在您的原始碼中輸出這些錯誤。您可以使用 typecheck.ignoreSourceErrors 配置選項來停用此功能。
請注意,Vitest 不會執行這些檔案,它們僅由編譯器進行靜態分析。這意味著,如果您使用動態名稱、test.each 或 test.for,測試名稱將不會被評估,而是會如實顯示。
WARNING
在 Vitest 2.1 之前,您的 typecheck.include 設定會覆寫 include 模式,導致您的執行時測試實際上並未執行,而僅進行了型別檢查。
自 Vitest 2.1 起,如果您的 include 和 typecheck.include 模式重疊,Vitest 將把型別測試和執行時測試報告為獨立項目。
型別檢查也支援 CLI 參數,例如 --allowOnly 和 -t。
import { assertType, expectTypeOf } from 'vitest';
import { mount } from './mount.js';
test('我的型別運作正常', () => {
expectTypeOf(mount).toBeFunction();
expectTypeOf(mount).parameter(0).toMatchTypeOf<{ name: string }>();
// @ts-expect-error name 應為字串型別
assertType(mount({ name: 42 }));
});測試檔案中觸發的任何型別錯誤都將被視為測試失敗,因此您可以使用任何型別技巧來測試專案的型別。
您可以在 API 部分 中查看所有可用的匹配器。
讀取錯誤訊息
如果您正在使用 expectTypeOf API,請參閱 expect-type 文件中關於其錯誤訊息的說明。
當型別不匹配時,.toEqualTypeOf 和 .toMatchTypeOf 會使用一個特殊的輔助型別來產生盡可能清晰的錯誤訊息。但理解它們有一些細微之處。由於斷言是以「鏈式呼叫」方式撰寫的,因此失敗應該發生在「預期」型別上,而不是「實際」型別上 (expect<Actual>().toEqualTypeOf<Expected>())。這意味著型別錯誤可能會有點令人困惑,因此這個套件會產生一個 MismatchInfo 型別,試圖清楚說明預期是什麼。例如:
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}>()請注意,報告的型別約束是易於理解的訊息,明確指定了「預期」和「實際」型別。與其拘泥於字面理解 Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number" 這句話,不如直接查看屬性名稱 ('a') 和訊息:Expected: string, Actual: number。在大多數情況下,這能指出問題所在。極其複雜的型別當然需要更多的精力來除錯,並且可能需要一些實驗。如果錯誤訊息實際上具有誤導性,請回報問題。
toBe... 方法(例如 toBeString、toBeNumber、toBeVoid 等)透過在測試中的 Actual 型別不匹配時解析為不可呼叫的型別而失敗。例如,像 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" 型別 的支援,這些錯誤訊息可以顯著改進。在此之前,理解這些錯誤訊息仍需一番琢磨。
具體「預期」物件與型別參數
對於像這樣的斷言,其錯誤訊息:
expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' });將不如像這樣的斷言的錯誤訊息有用:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();這是因為 TypeScript 編譯器需要為 .toEqualTypeOf({a: ''}) 樣式推斷型別參數,而這個套件只能透過將其與通用 Mismatch 型別進行比較來將其標記為失敗。因此,在可能的情況下,.toEqualTypeOf 和 toMatchTypeOf 應使用型別參數而非具體型別。如果比較兩個具體型別更方便,則可以使用 typeof:
const one = valueFromFunctionOne({ some: { complex: inputs } });
const two = valueFromFunctionTwo({ some: { other: inputs } });
expectTypeOf(one).toEqualTypeOf<typeof two>();如果您覺得使用 expectTypeOf API 並理解錯誤訊息很困難,您始終可以使用更簡單的 assertType API:
const answer = 42;
assertType<number>(answer);
// @ts-expect-error answer 不是字串
assertType<string>(answer);TIP
當使用 @ts-expect-error 語法時,您可能希望確保沒有打錯字。您可以透過將型別檔案包含在 test.include 配置選項中來確保這一點,這樣 Vitest 也會實際 執行 這些測試並因 ReferenceError 而失敗。
這段程式碼將會通過,因為它預期會出現錯誤,但「answer」這個詞有錯字,因此這是一個誤報:
// @ts-expect-error answer 不是字串
assertType<string>(answr);執行型別檢查
要啟用型別檢查,只需在 package.json 中的 Vitest 命令中添加 --typecheck 旗標:
{
"scripts": {
"test": "vitest --typecheck"
}
}現在您可以執行型別檢查:
npm run testyarn testpnpm run testbun testVitest 會根據您的配置使用 tsc --noEmit 或 vue-tsc --noEmit,因此您可以從您的流程中移除這些指令碼。