第10章 Vitest 类型测试指南:使用 expectTypeOf 与 assertType
测试类型 Sample ProjectGitHub - Play Online Vitest 允许你使用 `expectTypeOf` 或 `assertType` 语法来为类型编写测试。默认情况下,`*.test-d.ts` 文件中的所有测试都被视为类型测试,但你可以通过 `typecheck.include` 配置项进行修改。
在底层,Vitest 会根据你的配置调用 `tsc` 或 `vue-tsc`,并解析结果。如果检测到源代码中存在类型错误,Vitest 也会将其打印出来。你可以通过 `typecheck.ignoreSourceErrors` 配置项禁用此功能。
请注意,Vitest 不会运行这些文件,它们仅由编译器进行静态分析。这意味着,如果你使用动态名称或 `test.each`、`test.for`,测试名称不会被求值——它将按原样显示。
警告 在 Vitest 2.1 之前,`typecheck.include` 会覆盖 `include` 模式,导致运行时测试实际上并未运行;它们只进行了类型检查。
从 Vitest 2.1 开始,如果 `include` 和 `typecheck.include` 重叠,Vitest 会将类型测试和运行时测试作为独立条目报告。
类型检查也支持 `--allowOnly` 和 `-t` 等 CLI 标志。
`mount.test-d.ts` ```ts import { assertType, expectTypeOf } from 'vitest' import { mount } from './mount.js'
test('my types work properly', () => { expectTypeOf(mount).toBeFunction() expectTypeOf(mount).parameter(0).toExtend<{ name: string }>()
// @ts-expect-error name is a string assertType(mount({ name: 42 })) }) ``` 测试文件中触发的任何类型错误都会被视为测试错误,因此你可以使用任何类型技巧来测试项目的类型。
你可以在 API 部分查看可能的匹配器列表。
读取错误 如果你使用 `expectTypeOf` API,请参考 `expect-type` 文档中关于其错误消息的内容。
当类型不匹配时,`.toEqualTypeOf` 和 `.toExtend` 使用一种特殊的辅助类型来生成尽可能可操作(actionable)的错误消息。但理解它们有一些细微之处。由于断言是以“流畅式”(fluently)编写的,失败应体现在“期望”类型上,而不是“实际”类型(即 `expect
```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}>() ``` 注意,报告的类型约束是一种人类可读的消息,指定了“期望”和“实际”类型。不要字面理解“Types of property 'a' are incompatible”(属性 'a' 的类型不兼容)或“Type 'string' is not assignable to type 'Expected: string, Actual: number'”(类型 'string' 不能分配给类型 'Expected: string, Actual: number')——只需查看属性名('a')和消息:Expected: string, Actual: number(期望:string,实际:number)。在大多数情况下,这会告诉你问题所在。当然,极其复杂的类型需要更多的调试努力,可能需要一些实验。如果错误消息确实具有误导性,请提出 issue。
`toBe...` 方法(如 `toBeString`、`toBeNumber`、`toBeVoid` 等)在待测的实际类型不匹配时,会通过解析为一个不可调用的类型来判定失败。例如,像 `expectTypeOf(1).toBeString()` 这样的断言,其失败形式如下:
```
test/test.ts:999:999 - error TS2349: This expression is not callable.
Type 'ExpectString
999 expectTypeOf(1).toBeString()
~~~~~~~~~~
```
“This expression is not callable”(此表达式不可调用)这部分并不是很有帮助——有意义的错误在下一行:“Type 'ExpectString
如果 TypeScript 支持“throw”类型,这些错误消息可以显著改进。在那之前,它们需要稍加仔细查看才能理解。
具体的“期望”对象 vs 类型参数 像这样断言的错误消息:
```ts expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' }) ``` 将不如这种断言有帮助:
```ts expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>() ``` 这是因为 TypeScript 编译器需要推断 `.toEqualTypeOf({a: ''})` 这种风格中的类型参数,而该库只能通过将其与通用的 `Mismatch` 类型进行比较来标记为失败。因此,尽可能使用类型参数而不是具体类型来进行 `.toEqualTypeOf` 和 `.toExtend` 的比较。如果比较两个具体类型更方便,你可以使用 `typeof`:
```ts const one = valueFromFunctionOne({ some: { complex: inputs } }) const two = valueFromFunctionTwo({ some: { other: inputs } })
expectTypeOf(one).toEqualTypeOf
```ts const answer = 42
assertType
这应该会通过,因为它期望一个错误,但单词 “answer” 有拼写错误,导致误报:
```ts
// @ts-expect-error answer is not a string
assertType
运行类型检查 要启用类型检查,只需在 `package.json` 中的 Vitest 命令添加 `--typecheck` 标志:
`package.json` ```json { "scripts": { "test": "vitest --typecheck" } } ``` 现在你可以运行类型检查: npm / yarn / pnpm / bun
```bash npm run test yarn test pnpm run test bun test ``` Vitest 根据你的配置使用 `tsc --noEmit` 或 `vue-tsc --noEmit`,因此你可以从管道中移除这些脚本。