Testy typów
Przykładowy projekt
Vitest umożliwia pisanie testów dla Twoich typów, wykorzystując składnię expectTypeOf lub assertType. Domyślnie wszystkie pliki z rozszerzeniem *.test-d.ts są traktowane jako testy typów, ale możesz to zmienić za pomocą opcji konfiguracyjnej typecheck.include.
Wewnętrznie Vitest wywołuje tsc lub vue-tsc, w zależności od konfiguracji, i analizuje ich wyniki. Vitest wyświetli również błędy typów znalezione w Twoim kodzie źródłowym. Możesz to wyłączyć za pomocą opcji konfiguracyjnej typecheck.ignoreSourceErrors.
Pamiętaj, że Vitest nie uruchamia tych plików; są one jedynie statycznie analizowane przez kompilator. Oznacza to, że jeśli użyjesz dynamicznej nazwy testu, test.each lub test.for, nazwa testu nie zostanie oceniona – zostanie wyświetlona w niezmienionej formie.
WARNING
Przed Vitest 2.1, Twoja opcja typecheck.include nadpisywała wzorzec include, co oznaczało, że Twoje testy wykonawcze faktycznie się nie uruchamiały; były tylko sprawdzane pod kątem typów.
Od Vitest 2.1, jeśli Twoje opcje include i typecheck.include nakładają się, Vitest zgłosi testy typów i testy wykonawcze jako osobne pozycje.
Używanie flag CLI, takich jak --allowOnly i -t, jest również wspierane podczas sprawdzania typów.
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 }));
});Każdy błąd typu wykryty w pliku testowym będzie traktowany jako błąd testu, co pozwala na użycie dowolnej techniki typowania do testowania typów Twojego projektu.
Listę dostępnych matcherów znajdziesz w sekcji API.
Interpretacja błędów
Jeśli używasz API expectTypeOf, zapoznaj się z dokumentacją expect-type dotyczącą komunikatów o błędach.
Gdy typy się nie zgadzają, .toEqualTypeOf i .toMatchTypeOf używają specjalnego typu pomocniczego do generowania jak najbardziej pomocnych komunikatów o błędach. Jednak ich zrozumienie wymaga pewnego niuansu. Ponieważ asercje są pisane w sposób "płynny" (fluent API), błąd powinien dotyczyć typu "oczekiwanego", a nie typu "rzeczywistego" (expect<Actual>().toEqualTypeOf<Expected>()). Oznacza to, że błędy typów mogą być nieco mylące – dlatego ta biblioteka generuje typ MismatchInfo, aby wyraźnie określić, jakie jest oczekiwanie. Na przykład:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();Jest to asercja, która zakończy się niepowodzeniem, ponieważ {a: 1} ma typ {a: number}, a nie {a: string}. Komunikat o błędzie w tym przypadku będzie wyglądał mniej więcej tak:
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}>()Zauważ, że zgłoszone ograniczenie typu to zrozumiały dla ludzi komunikat określający zarówno typy "oczekiwane", jak i "rzeczywiste". Zamiast dosłownie traktować zdanie Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number", wystarczy spojrzeć na nazwę właściwości ('a') i komunikat: Expected: string, Actual: number. W większości przypadków to powie Ci, co jest nie tak. Niezwykle złożone typy będą oczywiście wymagały większego wysiłku w debugowaniu i mogą wymagać eksperymentów. Proszę zgłoś problem, jeśli komunikaty o błędach są faktycznie mylące.
Metody typu toBe... (takie jak toBeString, toBeNumber, toBeVoid itp.) kończą się niepowodzeniem, zwracając typ nie-wywoływalny, gdy testowany typ Actual nie pasuje. Na przykład, błąd dla asercji takiej jak expectTypeOf(1).toBeString() będzie wyglądał mniej więcej tak:
test/test.ts:999:999 - error TS2349: This expression is not callable.
Type 'ExpectString<number>' has no call signatures.
999 expectTypeOf(1).toBeString()
~~~~~~~~~~Część This expression is not callable nie jest zbyt pomocna – istotny błąd to następna linia, Type 'ExpectString<number> has no call signatures. Oznacza to zasadniczo, że przekazałeś liczbę, ale stwierdziłeś, że powinna być ciągiem znaków.
Gdyby TypeScript dodał obsługę "throw" types, te komunikaty o błędach mogłyby zostać znacznie ulepszone. Do tego czasu będą wymagały pewnej dozy wysiłku w ich interpretacji.
Konkretne obiekty "oczekiwane" vs. parametry typów
Komunikaty o błędach dla asercji takiej jak:
expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' });Będą mniej pomocne niż dla asercji takiej jak:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();Dzieje się tak, ponieważ kompilator TypeScript musi wywnioskować parametr typu dla stylu .toEqualTypeOf({a: ''}), a ta biblioteka może oznaczyć to jako błąd jedynie poprzez porównanie go z ogólnym typem Mismatch. Dlatego, jeśli to możliwe, używaj parametru typu zamiast konkretnego typu dla .toEqualTypeOf i toMatchTypeOf. Jeśli znacznie wygodniej jest porównać dwa konkretne typy, możesz użyć operatora typeof:
const one = valueFromFunctionOne({ some: { complex: inputs } });
const two = valueFromFunctionTwo({ some: { other: inputs } });
expectTypeOf(one).toEqualTypeof<typeof two>();Jeśli masz trudności z pracą z API expectTypeOf i interpretacją błędów, zawsze możesz skorzystać z prostszego API assertType:
const answer = 42;
assertType<number>(answer);
// @ts-expect-error answer is not a string
assertType<string>(answer);TIP
Używając składni @ts-expect-error, powinieneś upewnić się, że nie popełniłeś literówki. Możesz to zrobić, włączając swoje pliki typów do opcji konfiguracyjnej test.include, dzięki czemu Vitest faktycznie uruchomi te testy i zakończy się błędem ReferenceError.
Test zostanie zaliczony, ponieważ oczekuje błędu, ale słowo „answer” zawiera literówkę, co prowadzi do fałszywie pozytywnego błędu:
// @ts-expect-error answer is not a string
assertType<string>(answr); //Uruchom sprawdzanie typów
Aby włączyć sprawdzanie typów, po prostu dodaj flagę --typecheck do swojego polecenia Vitest w package.json:
{
"scripts": {
"test": "vitest --typecheck"
}
}Teraz możesz uruchomić sprawdzanie typów:
npm run testyarn testpnpm run testbun testVitest używa tsc --noEmit lub vue-tsc --noEmit, w zależności od konfiguracji, więc możesz usunąć te skrypty ze swojego potoku.