Tipi di Test
Progetto Esempio
Vitest ti consente di scrivere test per i tuoi tipi, utilizzando le sintassi expectTypeOf
o assertType
. Per impostazione predefinita, tutti i test all'interno dei file *.test-d.ts
sono considerati test sui tipi, ma puoi modificare questa impostazione tramite l'opzione di configurazione typecheck.include
.
Internamente, Vitest invoca tsc
o vue-tsc
, a seconda della tua configurazione, ed elabora i risultati. Vitest visualizzerà anche gli errori di tipo presenti nel tuo codice sorgente, qualora ne trovasse. Puoi disabilitare questa funzionalità con l'opzione di configurazione typecheck.ignoreSourceErrors
.
È importante notare che Vitest non esegue questi file; essi vengono solo analizzati staticamente dal compilatore. Ciò implica che se utilizzi un nome dinamico, test.each
o test.for
, il nome del test non verrà valutato, ma mostrato così com'è.
WARNING
Prima di Vitest 2.1, la tua configurazione typecheck.include
sovrascriveva il modello include
, impedendo l'esecuzione effettiva dei tuoi test di runtime, che venivano solo sottoposti a verifica dei tipi.
A partire da Vitest 2.1, se le tue configurazioni include
e typecheck.include
si sovrappongono, Vitest riporterà i test di tipo e i test di runtime come voci separate.
L'uso di flag CLI, come --allowOnly
e -t
, è supportato anche per il controllo dei tipi.
import { assertType, expectTypeOf } from 'vitest';
import { mount } from './mount.js';
test('i miei tipi sono corretti', () => {
expectTypeOf(mount).toBeFunction();
expectTypeOf(mount).parameter(0).toMatchTypeOf<{ name: string }>();
// @ts-expect-error name è una stringa
assertType(mount({ name: 42 }));
});
Qualsiasi errore di tipo rilevato all'interno di un file di test verrà trattato come un errore di test, permettendoti di utilizzare qualsiasi tecnica di tipizzazione desideri per testare i tipi del tuo progetto.
Puoi consultare un elenco dei possibili matcher nella sezione API.
Lettura degli Errori
Se stai utilizzando l'API expectTypeOf
, fai riferimento alla documentazione di expect-type sui suoi messaggi di errore.
Quando i tipi non corrispondono, .toEqualTypeOf
e .toMatchTypeOf
utilizzano un tipo helper speciale per produrre messaggi di errore il più utili possibile. Tuttavia, c'è una sottile sfumatura nella loro interpretazione. Poiché le asserzioni sono scritte in modo "fluente", l'errore dovrebbe essere attribuito al tipo "atteso", non al tipo "attuale" (expect<Actual>().toEqualTypeOf<Expected>()
). Ciò significa che gli errori di tipo possono essere un po' confusi; per questo motivo, questa libreria produce un tipo MismatchInfo
per rendere esplicita l'aspettativa. Ad esempio:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();
Questa è un'asserzione che fallirà, poiché {a: 1}
ha tipo {a: number}
e non {a: string}
. Il messaggio di errore in questo caso sarà simile a:
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}>()
Nota che il vincolo di tipo riportato è un messaggio leggibile che specifica sia i tipi "atteso" che "attuale". Invece di prendere letteralmente la frase Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number"
, concentrati sul nome della proprietà ('a'
) e sul messaggio: Expected: string, Actual: number
. Questo ti indicherà l'errore nella maggior parte dei casi. Tipi estremamente complessi richiederanno ovviamente maggiore sforzo per il debug e potrebbero richiedere diversi tentativi. Per favore, apri un'issue se i messaggi di errore sono effettivamente fuorvianti.
I metodi toBe...
(come toBeString
, toBeNumber
, toBeVoid
ecc.) falliscono risolvendo a un tipo non richiamabile quando il tipo Actual
sotto test non corrisponde. Ad esempio, il fallimento per un'asserzione come expectTypeOf(1).toBeString()
sarà simile a:
test/test.ts:999:999 - error TS2349: This expression is not callable.
Type 'ExpectString<number>' has no call signatures.
999 expectTypeOf(1).toBeString()
~~~~~~~~~~
La parte This expression is not callable
non è molto utile; l'errore più significativo è la riga successiva: Type 'ExpectString<number>' has no call signatures
. Questo significa essenzialmente che hai passato un numero ma hai asserito che dovrebbe essere una stringa.
Se TypeScript aggiungesse il supporto per i "throw" types, questi messaggi di errore potrebbero essere notevolmente migliorati. Fino ad allora, richiederanno un'attenta analisi.
Oggetti "attesi" concreti vs typeargs
I messaggi di errore per un'asserzione come questa:
expectTypeOf({ a: 1 }).toEqualTypeOf({ a: '' });
Saranno meno utili rispetto a un'asserzione come questa:
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: string }>();
Questo perché il compilatore TypeScript deve inferire il typearg per lo stile .toEqualTypeOf({a: ''})
, e questa libreria può solo contrassegnarlo come un fallimento confrontandolo con un tipo Mismatch
generico. Pertanto, ove possibile, utilizza un typearg piuttosto che un tipo concreto per .toEqualTypeOf
e toMatchTypeOf
. Se è molto più conveniente confrontare due tipi concreti, puoi usare typeof
:
const one = valueFromFunctionOne({ some: { complex: inputs } });
const two = valueFromFunctionTwo({ some: { other: inputs } });
expectTypeOf(one).toEqualTypeOf<typeof two>();
Se trovi difficile lavorare con l'API expectTypeOf
e comprendere gli errori, puoi sempre utilizzare l'API più semplice assertType
:
const answer = 42;
assertType<number>(answer);
// @ts-expect-error answer non è una stringa
assertType<string>(answer);
TIP
Quando utilizzi la sintassi @ts-expect-error
, potresti voler assicurarti di non aver commesso un errore di battitura. Puoi farlo includendo i tuoi file di definizione dei tipi nell'opzione di configurazione test.include
, in modo che Vitest esegua effettivamente questi test e fallisca con un ReferenceError
.
Questo sarà accettato, perché si aspetta un errore, ma la parola "answer" ha un errore di battitura, il che porta a un falso positivo:
// @ts-expect-error answer non è una stringa
assertType<string>(answr);
Esegui il Controllo dei Tipi
Per abilitare il controllo dei tipi, aggiungi semplicemente il flag --typecheck
al tuo comando Vitest in package.json
:
{
"scripts": {
"test": "vitest --typecheck"
}
}
Ora puoi eseguire il controllo dei tipi:
npm run test
yarn test
pnpm run test
bun test
Vitest utilizza tsc --noEmit
o vue-tsc --noEmit
, a seconda della tua configurazione, quindi puoi rimuovere questi script dalla tua pipeline.