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 test
yarn test
pnpm run test
bun test
Vitest używa tsc --noEmit
lub vue-tsc --noEmit
, w zależności od Twojej konfiguracji, więc możesz usunąć te skrypty z potoku.