Typüberprüfungen
Beispielprojekt
Vitest ermöglicht es Ihnen, Tests für Ihre Typen zu schreiben, indem Sie die expectTypeOf
- oder assertType
-Syntax verwenden. Standardmäßig werden alle Tests in *.test-d.ts
-Dateien als Typentests behandelt, aber Sie können dies mit der Konfigurationsoption typecheck.include
anpassen.
Intern ruft Vitest tsc
oder vue-tsc
auf, abhängig von Ihrer Konfiguration, und analysiert die Ergebnisse. Vitest zeigt auch Typfehler in Ihrem Quellcode an, falls vorhanden. Sie können dies mit der Konfigurationsoption typecheck.ignoreSourceErrors
deaktivieren.
Bitte beachten Sie, dass Vitest diese Dateien nicht ausführt; sie werden lediglich statisch vom Compiler analysiert. Das bedeutet: Wenn Sie einen dynamischen Namen, test.each
oder test.for
verwenden, wird der Testname nicht ausgewertet – er wird so angezeigt, wie er im Code steht.
WARNING
Vor Vitest 2.1 überschrieb Ihr typecheck.include
das include
-Muster, was dazu führte, dass Ihre Laufzeittests nicht ausgeführt, sondern nur auf Typen geprüft wurden.
Ab Vitest 2.1 gilt: Wenn sich Ihr include
und typecheck.include
überschneiden, meldet Vitest Typentests und Laufzeittests als separate Einträge.
CLI-Flags wie --allowOnly
und -t
werden auch für die Typüberprüfung unterstützt.
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 ist ein String
assertType(mount({ name: 42 }));
});
Jeder Typfehler, der in einer Testdatei auftritt, wird als Testfehler behandelt, sodass Sie jeden gewünschten Typ-Trick anwenden können, um die Typen Ihres Projekts zu testen.
Eine Liste der möglichen Matcher finden Sie im Abschnitt API.
Fehler verstehen
Wenn Sie die expectTypeOf
-API verwenden, beachten Sie die expect-type-Dokumentation zu ihren Fehlermeldungen.
Wenn Typen nicht übereinstimmen, verwenden .toEqualTypeOf
und .toMatchTypeOf
einen speziellen Helfertyp, um möglichst aussagekräftige Fehlermeldungen zu erzeugen. Es gibt jedoch eine kleine Nuance beim Verständnis dieser Fehlermeldungen. Da die Assertions "fließend" (chained) geschrieben sind, sollte der Fehler beim "erwarteten" Typ liegen, nicht beim "tatsächlichen" Typ (expect<Actual>().toEqualTypeOf<Expected>()
). Das bedeutet, dass Typfehler etwas verwirrend sein können – daher erzeugt diese Bibliothek einen MismatchInfo
-Typ, um die Erwartung explizit darzustellen. Zum Beispiel:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();
Diese Assertion schlägt fehl, da {a: 1}
den Typ {a: number}
und nicht {a: string}
hat. Die Fehlermeldung in diesem Fall wird etwa so lauten:
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}>()
Beachten Sie, dass die gemeldete Typeneinschränkung eine menschenlesbare Nachricht ist, die sowohl den "erwarteten" als auch den "tatsächlichen" Typ angibt. Anstatt den Satz Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number"
buchstäblich zu interpretieren – schauen Sie einfach auf den Eigenschaftsnamen ('a'
) und die Nachricht: Expected: string, Actual: number
. Dies wird Ihnen in den meisten Fällen aufzeigen, was falsch ist. Extrem komplexe Typen erfordern natürlich mehr Aufwand beim Debuggen und können Experimente erfordern. Bitte melden Sie ein Problem, wenn die Fehlermeldungen tatsächlich irreführend sind.
Die toBe...
-Methoden (wie toBeString
, toBeNumber
, toBeVoid
usw.) schlagen fehl, indem sie zu einem nicht aufrufbaren Typ aufgelöst werden, wenn der Actual
-Typ, der getestet wird, nicht übereinstimmt. Zum Beispiel sieht der Fehler für eine Assertion wie expectTypeOf(1).toBeString()
etwa so aus:
test/test.ts:999:999 - error TS2349: This expression is not callable.
Type 'ExpectString<number>' has no call signatures.
999 expectTypeOf(1).toBeString()
~~~~~~~~~~
Der Teil This expression is not callable
ist wenig hilfreich – der aussagekräftige Fehler ist die nächste Zeile: Type 'ExpectString<number>' has no call signatures
. Dies bedeutet im Wesentlichen, dass Sie eine Zahl übergeben haben, aber einen String erwartet haben.
Wenn TypeScript Unterstützung für "throw" types hinzufügen würde, könnten diese Fehlermeldungen erheblich verbessert werden. Bis dahin erfordern sie ein gewisses Maß an genauer Betrachtung.
Konkrete "erwartete" Objekte vs. Typargumente
Fehlermeldungen für eine Assertion wie diese:
expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' });
Werden weniger hilfreich sein als für eine Assertion wie diese:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();
Dies liegt daran, dass der TypeScript-Compiler das Typargument für den Stil .toEqualTypeOf({a: ''})
inferieren muss, und diese Bibliothek kann es nur als Fehler markieren, indem sie es mit einem generischen Mismatch
-Typ vergleicht. Verwenden Sie daher, wo immer möglich, ein Typargument anstelle eines konkreten Typs für .toEqualTypeOf
und toMatchTypeOf
. Wenn es viel bequemer ist, zwei konkrete Typen zu vergleichen, können Sie typeof
verwenden:
const one = valueFromFunctionOne({ some: { complex: inputs } });
const two = valueFromFunctionTwo({ some: { other: inputs } });
expectTypeOf(one).toEqualTypeOf<typeof two>();
Wenn Sie Schwierigkeiten haben, mit der expectTypeOf
-API zu arbeiten und Fehler zu finden, können Sie jederzeit die einfachere assertType
-API verwenden:
const answer = 42;
assertType<number>(answer);
// @ts-expect-error answer ist kein String
assertType<string>(answer);
TIP
Wenn Sie die @ts-expect-error
-Syntax verwenden, sollten Sie sicherstellen, dass Sie keinen Tippfehler gemacht haben. Dies können Sie tun, indem Sie Ihre Typdateien in die Konfigurationsoption test.include
aufnehmen, sodass Vitest diese Tests tatsächlich ausführt und mit einem ReferenceError
fehlschlägt.
Dies wird bestehen, weil ein Fehler erwartet wird, aber das Wort „answr“ einen Tippfehler hat, sodass es ein falsch positiver Fehler ist:
// @ts-expect-error answer ist kein String
assertType<string>(answr);
Typüberprüfung ausführen
Um die Typüberprüfung zu aktivieren, fügen Sie einfach das Flag --typecheck
zu Ihrem Vitest-Befehl in package.json
hinzu:
{
"scripts": {
"test": "vitest --typecheck"
}
}
Jetzt können Sie die Typüberprüfung ausführen:
npm run test
yarn test
pnpm run test
bun test
Vitest verwendet tsc --noEmit
oder vue-tsc --noEmit
, abhängig von Ihrer Konfiguration, sodass Sie diese Skripte aus Ihrer Pipeline entfernen können.