Typy testów
Przykładowy projekt
Vitest umożliwia pisanie testów typów, wykorzystując składnię expectTypeOf lub assertType. Domyślnie wszystkie pliki *.test-d.ts są traktowane jako testy typów, ale możesz to zmienić za pomocą opcji konfiguracji typecheck.include.
Wewnętrznie Vitest wywołuje tsc lub vue-tsc, w zależności od Twojej konfiguracji, a następnie parsuje 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 lub test.each, test.for, nazwa testu nie zostanie oceniona i zostanie wyświetlona w niezmienionej formie.
WARNING
Przed Vitest 2.1, Twoja konfiguracja typecheck.include nadpisywała wzorzec include, co oznaczało, że Twoje testy wykonawcze nie były faktycznie uruchamiane; były jedynie sprawdzane pod kątem typów.
Od Vitest 2.1, jeśli Twoje include i typecheck.include nakładają się, Vitest zgłosi testy typów i testy wykonawcze jako oddzielne wpisy.
Flagi CLI, takie jak --allowOnly i -t, są również obsługiwane w przypadku 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 wywołany w pliku testowym zostanie potraktowany jako błąd testu, co pozwala na wykorzystanie dowolnych technik typowania do testowania typów Twojego projektu.
Listę dostępnych matcherów znajdziesz w sekcji API.
Odczytywanie błędów
Jeśli używasz API expectTypeOf, zapoznaj się z dokumentacją expect-type dotyczącą komunikatów o błędach.
Gdy typy nie pasują, .toEqualTypeOf i .toMatchTypeOf używają specjalnego typu pomocniczego do generowania jak najbardziej użytecznych komunikatów o błędach. Jednak ich zrozumienie wymaga pewnego niuansu. Ponieważ asercje są pisane w sposób płynny, błąd powinien dotyczyć typu "oczekiwanego", a nie typu "rzeczywistego" (expect<Actual>().toEqualTypeOf<Expected>()). Oznacza to, że błędy typów mogą być mylące, dlatego ta biblioteka generuje typ MismatchInfo, aby wyraźnie określić oczekiwania. 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 miał następującą postać:
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 czytelny komunikat, który określa zarówno typ "oczekiwany", jak i "rzeczywisty". Zamiast brać zdanie Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number" dosłownie, po prostu spójrz na nazwę właściwości ('a') i komunikat: Expected: string, Actual: number. To wskaże Ci, co jest nie tak, w większości przypadków. Niezwykle złożone typy będą oczywiście wymagały większego wysiłku w debugowaniu i mogą wymagać pewnych eksperymentów. Proszę zgłoś problem, jeśli komunikaty o błędach są faktycznie mylące.
Metody toBe... (np. toBeString, toBeNumber, toBeVoid itp.) kończą się niepowodzeniem, rozwiązując się do typu nie-wywoływalnego, 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()
~~~~~~~~~~Fragment This expression is not callable nie jest zbyt pomocny; istotny błąd znajduje się w następnej linii, Type 'ExpectString<number>' has no call signatures. W zasadzie oznacza to, że przekazano liczbę, ale zaasertowano, że powinna być stringiem.
Gdyby TypeScript dodał obsługę "throw" types, te komunikaty o błędach mogłyby zostać znacznie ulepszone. Do tego czasu będą wymagały pewnego skupienia przy interpretacji.
Konkretne obiekty "oczekiwane" vs. argumenty typów
Komunikaty o błędach dla asercji typu:
expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' });Będą mniej pomocne niż dla asercji takiej jak:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();Wynika to z faktu, że kompilator TypeScript musi wywnioskować argument typu dla stylu .toEqualTypeOf({a: ''}), a ta biblioteka może oznaczyć go jako błąd tylko poprzez porównanie go z ogólnym typem Mismatch. Dlatego, jeśli to możliwe, używaj argumentu typu zamiast konkretnego typu dla .toEqualTypeOf i toMatchTypeOf. Jeśli porównywanie dwóch konkretnych typów jest znacznie wygodniejsze, możesz użyć typeof:
const one = valueFromFunctionOne({ some: { complex: inputs } });
const two = valueFromFunctionTwo({ some: { other: inputs } });
expectTypeOf(one).toEqualTypeOf<typeof two>();Jeśli masz problemy z użyciem API expectTypeOf i rozszyfrowaniem 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, warto 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.
To przejdzie, ponieważ oczekuje błędu, ale w słowie „answer” jest literówka, więc jest to fałszywie pozytywny błąd:
// @ts-expect-error answer is not a string
assertType<string>(answr);Uruchamianie sprawdzania typów
Aby włączyć sprawdzanie typów, wystarczy dodać flagę --typecheck do polecenia Vitest w pliku 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 Twojej konfiguracji, więc możesz usunąć te skrypty z potoku.