Tests de Type
Projet d'Exemple
Vitest vous permet d'écrire des tests pour vos types, en utilisant les syntaxes expectTypeOf
ou assertType
. Par défaut, tous les fichiers *.test-d.ts
sont considérés comme des tests de type, mais vous pouvez modifier ce comportement avec l'option de configuration typecheck.include
.
En arrière-plan, Vitest exécute tsc
ou vue-tsc
, selon votre configuration, et analyse les résultats. Vitest affichera également les erreurs de type détectées dans votre code source. Vous pouvez désactiver cette fonctionnalité avec l'option de configuration typecheck.ignoreSourceErrors
.
Notez que Vitest n'exécute ni ne compile ces fichiers. Ils sont uniquement analysés statiquement par le compilateur. Par conséquent, vous ne pouvez pas utiliser d'instructions ou d'opérations dynamiques dans ces fichiers. Cela signifie que vous ne pouvez pas utiliser de noms de test dynamiques, ni les APIs test.each
, test.runIf
, test.skipIf
, test.concurrent
. Cependant, vous pouvez utiliser d'autres APIs, comme test
, describe
, .only
, .skip
et .todo
.
L'utilisation des options CLI, comme --allowOnly
et -t
, est également prise en charge pour la vérification de type.
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 }));
});
Toute erreur de type détectée dans un fichier de test sera traitée comme un échec de test. Vous pouvez donc utiliser n'importe quelle technique de typage pour tester les types de votre projet.
Vous pouvez consulter une liste des matchers disponibles dans la section API.
Interprétation des Erreurs
Si vous utilisez l'API expectTypeOf
, référez-vous à la documentation expect-type concernant ses messages d'erreur.
Lorsque les types ne correspondent pas, .toEqualTypeOf
et .toMatchTypeOf
utilisent un type d'assistance spécial pour générer des messages d'erreur aussi précis que possible. Il y a cependant une subtilité à comprendre. Puisque les assertions sont écrites de manière "fluide", l'erreur doit se trouver sur le type "attendu", et non sur le type "réel" (expect<Actual>().toEqualTypeOf<Expected>()
). Cela signifie que les erreurs de type peuvent être légèrement déroutantes. Cette bibliothèque génère donc un type MismatchInfo
pour rendre l'attente plus explicite. Par exemple :
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();
Cette assertion échouera, car {a: 1}
a le type {a: number}
et non {a: string}
. Le message d'erreur dans ce cas ressemblera à ceci :
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}>()
Notez que la contrainte de type rapportée est un message clair spécifiant à la fois les types "attendu" et "réel". Plutôt que d'interpréter littéralement la phrase Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number"
, concentrez-vous sur le nom de la propriété ('a'
) et le message : Expected: string, Actual: number
. Cela vous indiquera la cause du problème dans la plupart des cas. Les types extrêmement complexes nécessiteront bien sûr plus d'efforts de débogage et pourront nécessiter une certaine expérimentation. Veuillez ouvrir un ticket si les messages d'erreur sont réellement trompeurs.
Les méthodes toBe...
(comme toBeString
, toBeNumber
, toBeVoid
etc.) échouent en retournant un type non appelable lorsque le type Actual
testé ne correspond pas. Par exemple, l'échec d'une assertion comme expectTypeOf(1).toBeString()
ressemblera à ceci :
test/test.ts:999:999 - error TS2349: This expression is not callable.
Type 'ExpectString<number>' has no call signatures.
999 expectTypeOf(1).toBeString()
~~~~~~~~~~
La partie This expression is not callable
n'est pas très utile. L'erreur significative est la ligne suivante, Type 'ExpectString<number> has no call signatures
. Cela signifie essentiellement que vous avez passé un nombre mais que vous avez affirmé qu'il devait s'agir d'une chaîne de caractères.
Si TypeScript ajoutait la prise en charge des types "throw", ces messages d'erreur pourraient être considérablement améliorés. En attendant, il faudra s'y faire.
Objets "attendus" concrets vs arguments de type (typeargs)
Les messages d'erreur pour une assertion comme celle-ci :
expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' });
Seront moins utiles que pour une assertion comme celle-ci :
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();
Cela est dû au fait que le compilateur TypeScript doit inférer le typearg pour le style .toEqualTypeOf({a: ''})
, et cette bibliothèque ne peut le marquer comme un échec qu'en le comparant à un type Mismatch
générique. Par conséquent, quand c'est possible, utilisez un argument de type (typearg) plutôt qu'un type concret pour .toEqualTypeOf
et toMatchTypeOf
. S'il est plus pratique de comparer deux types concrets, vous pouvez utiliser typeof
:
const one = valueFromFunctionOne({ some: { complex: inputs } });
const two = valueFromFunctionTwo({ some: { other: inputs } });
expectTypeOf(one).toEqualTypeof<typeof two>();
Si vous rencontrez des difficultés avec l'API expectTypeOf
et la compréhension des erreurs, vous pouvez toujours utiliser l'API assertType
plus simple :
const answer = 42;
assertType<number>(answer);
// @ts-expect-error answer is not a string
assertType<string>(answer);
TIP
Lorsque vous utilisez la syntaxe @ts-expect-error
, il est conseillé de vérifier que vous n'avez pas fait de faute de frappe. Vous pouvez le faire en incluant vos fichiers de type dans l'option de configuration test.include
, afin que Vitest exécute également ces tests et échoue avec ReferenceError
.
L'exemple ci-dessous réussira, car il s'attend à une erreur, mais le mot "answer" contient une faute de frappe, ce qui constitue une erreur de faux positif :
// @ts-expect-error answer is not a string
assertType<string>(answr); //
Exécuter la Vérification de Type
Depuis Vitest 1.0, pour activer la vérification de type, ajoutez simplement l'option --typecheck
à votre commande Vitest dans package.json
:
{
"scripts": {
"test": "vitest --typecheck"
}
}
Vous pouvez maintenant exécuter la vérification de type :
npm run test
yarn test
pnpm run test
bun test
Vitest utilise tsc --noEmit
ou vue-tsc --noEmit
, selon votre configuration. Vous pouvez donc les supprimer de votre chaîne de production.