測試類型
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('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
類型,以嘗試明確說明預期為何。例如:
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 is not a string
assertType<string>(answer);
TIP
使用 @ts-expect-error
語法時,您可能需要確保沒有拼寫錯誤。您可以透過將類型檔案包含在 test.include
配置選項中來做到這一點,這樣 Vitest 也會實際「執行」這些測試,並在發生 ReferenceError
時失敗。
這將會通過,因為它預期會出錯,但「answer」這個詞有拼寫錯誤,所以這是一個誤報錯誤:
// @ts-expect-error answer is not a string
assertType<string>(answr); //
執行類型檢查
要啟用類型檢查,只需在 package.json
中的 Vitest 命令中添加 --typecheck
參數即可:
{
"scripts": {
"test": "vitest --typecheck"
}
}
現在您可以執行類型檢查:
npm run test
yarn test
pnpm run test
bun test
Vitest 會根據您的配置使用 tsc --noEmit
或 vue-tsc --noEmit
,因此您可以從您的管道中移除這些指令碼。