타입 테스트
Vitest에서는 expectTypeOf 또는 assertType 구문을 사용하여 타입 테스트를 작성할 수 있습니다. 기본적으로 *.test-d.ts 파일 내의 모든 테스트는 타입 테스트로 간주되지만, typecheck.include 설정 옵션을 통해 이를 변경할 수 있습니다.
Vitest는 내부적으로 설정에 따라 tsc 또는 vue-tsc를 호출하고 그 결과를 분석합니다. 또한, 소스 코드에서 타입 오류를 발견하면 이를 출력합니다. 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}>()보고된 타입 제약은 '예상' 타입과 '실제' 타입을 모두 포함하는 읽기 쉬운 메시지입니다. 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" 타입에 대한 지원을 추가하면 이러한 오류 메시지를 크게 개선할 수 있습니다. 그때까지는 어느 정도 주의 깊게 살펴봐야 할 것입니다.
구체적인 "예상" 객체 vs 타입 인자
다음과 같은 단언문에 대한 오류 메시지:
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); //타입 검사 실행하기
Vitest 1.0부터 타입 검사를 활성화하려면 package.json에서 Vitest 명령에 --typecheck 플래그를 추가하면 됩니다.
{
"scripts": {
"test": "vitest --typecheck"
}
}이제 타입 검사를 실행할 수 있습니다.
npm run testyarn testpnpm run testbun testVitest는 설정에 따라 tsc --noEmit 또는 vue-tsc --noEmit를 사용하므로, 워크플로우에서 이러한 스크립트를 제거할 수 있습니다.