型テスト
Vitest では、expectTypeOf
や assertType
構文を用いて、型のテストを記述できます。デフォルトでは、*.test-d.ts
ファイル内のすべてのテストは型テストとみなされますが、typecheck.include
設定オプションで変更可能です。
内部的には、Vitest は設定に応じて tsc
または vue-tsc
を呼び出し、その結果を解析します。Vitest は、ソースコード内に型エラーが見つかった場合、それらも出力します。これは、typecheck.ignoreSourceErrors
設定オプションで無効化できます。
Vitest はこれらのファイルを実行またはコンパイルしないことに注意してください。これらはコンパイラによって静的に分析されるのみであるため、動的な記述は使用できません。つまり、動的なテスト名、test.each
、test.runIf
、test.skipIf
、test.concurrent
といった API は使用できません。ただし、test
、describe
、.only
、.skip
、.todo
などの API は使用できます。
--allowOnly
や -t
などの CLI フラグも、型チェックでサポートされています。
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}>()
報告される型制約は、"expected" 型と "actual" 型の両方を指定する、人間が読めるメッセージである点に注意してください。Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number"
という文を文字通りに解釈するのではなく、プロパティ名 ('a'
) とメッセージ Expected: string, Actual: number
を見てください。これにより、ほとんどの場合、何が問題かがわかります。非常に複雑な型は、もちろんデバッグに手間がかかり、場合によっては実験が必要になることもあります。エラーメッセージが実際に誤解を招く場合は、issue を立ててください。
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" types のサポートを追加した場合、これらのエラーメッセージは大幅に改善される可能性があります。それまでは、ある程度推測する必要があります。
具体的な "expected" オブジェクト vs 型引数
次のようなアサーションのエラーメッセージ:
expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' });
は、次のようなアサーションよりも有用ではありません:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();
これは、TypeScript コンパイラが .toEqualTypeOf({a: ''})
スタイルの型引数を推論する必要があり、このライブラリはそれを汎用的な Mismatch
型と比較することによってのみ失敗としてマークできるためです。したがって、可能な限り、.toEqualTypeOf
および .toMatchTypeOf
に具体的な型ではなく型引数を使用してください。2 つの具体的な型を比較する方がはるかに便利な場合は、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); //
型チェックの実行
Vitest 1.0 以降では、型チェックを有効にするには、package.json
の Vitest コマンドに --typecheck
フラグを追加するだけです:
{
"scripts": {
"test": "vitest --typecheck"
}
}
これで、型チェックを実行できます:
npm run test
yarn test
pnpm run test
bun test
Vitest は、設定に応じて tsc --noEmit
または vue-tsc --noEmit
を使用するため、これらのスクリプトをワークフローから削除できます。