0
# Type Testing
1
2
Vitest provides type-level testing capabilities for ensuring TypeScript types are correct without runtime execution.
3
4
## Capabilities
5
6
### ExpectTypeOf
7
8
Type-level assertions using the `expectTypeOf` function from the `expect-type` library.
9
10
```typescript { .api }
11
/**
12
* Create type-level expectation
13
* @param value - Value to check types for
14
* @returns Type-level assertion object
15
*/
16
function expectTypeOf<T>(value: T): ExpectTypeOf<T>;
17
function expectTypeOf<T>(): ExpectTypeOf<T>;
18
19
interface ExpectTypeOf<T> {
20
/** Assert type is exactly equal to Expected */
21
toEqualTypeOf<Expected>(): void;
22
23
/** Assert type matches Expected structure */
24
toMatchTypeOf<Expected>(): void;
25
26
/** Assert type is any */
27
toBeAny(): void;
28
29
/** Assert type is unknown */
30
toBeUnknown(): void;
31
32
/** Assert type is never */
33
toBeNever(): void;
34
35
/** Assert type is a function */
36
toBeFunction(): void;
37
38
/** Assert type is an object */
39
toBeObject(): void;
40
41
/** Assert type is an array */
42
toBeArray(): void;
43
44
/** Assert type is a string */
45
toBeString(): void;
46
47
/** Assert type is a number */
48
toBeNumber(): void;
49
50
/** Assert type is a boolean */
51
toBeBoolean(): void;
52
53
/** Assert type is void */
54
toBeVoid(): void;
55
56
/** Assert type is null */
57
toBeNull(): void;
58
59
/** Assert type is undefined */
60
toBeUndefined(): void;
61
62
/** Assert type is nullable (null or undefined) */
63
toBeNullable(): void;
64
65
/** Assert type has callable signature */
66
toBeCallableWith(...args: any[]): void;
67
68
/** Assert type can be constructed */
69
toBeConstructibleWith(...args: any[]): void;
70
71
/** Assert type has property */
72
toHaveProperty<K extends keyof T>(key: K): ExpectTypeOf<T[K]>;
73
74
/** Negate assertion */
75
not: ExpectTypeOf<T>;
76
77
/** Extract type from Promise */
78
resolves: T extends Promise<infer U> ? ExpectTypeOf<U> : never;
79
80
/** Extract parameters from function */
81
parameters: T extends (...args: infer P) => any ? ExpectTypeOf<P> : never;
82
83
/** Extract return type from function */
84
returns: T extends (...args: any[]) => infer R ? ExpectTypeOf<R> : never;
85
86
/** Extract type from array */
87
items: T extends (infer Item)[] ? ExpectTypeOf<Item> : never;
88
89
/** Extract branded type */
90
branded: ExpectTypeOf<T>;
91
}
92
```
93
94
**Usage:**
95
96
```typescript
97
import { test, expectTypeOf } from 'vitest';
98
99
test('type assertions', () => {
100
// Exact type equality
101
expectTypeOf<string>().toEqualTypeOf<string>();
102
expectTypeOf('hello').toEqualTypeOf<string>();
103
104
// Structural matching
105
expectTypeOf<{ a: number }>().toMatchTypeOf<{ a: number }>();
106
107
// Primitive types
108
expectTypeOf('').toBeString();
109
expectTypeOf(123).toBeNumber();
110
expectTypeOf(true).toBeBoolean();
111
expectTypeOf(null).toBeNull();
112
expectTypeOf(undefined).toBeUndefined();
113
expectTypeOf({}).toBeObject();
114
expectTypeOf([]).toBeArray();
115
expectTypeOf(() => {}).toBeFunction();
116
117
// Never and any
118
expectTypeOf<never>().toBeNever();
119
expectTypeOf<any>().toBeAny();
120
expectTypeOf<unknown>().toBeUnknown();
121
122
// Nullable
123
expectTypeOf<string | null>().toBeNullable();
124
expectTypeOf<string | undefined>().toBeNullable();
125
126
// Negation
127
expectTypeOf<number>().not.toBeString();
128
expectTypeOf<string>().not.toBeNumber();
129
});
130
```
131
132
### Function Type Testing
133
134
Test function signatures, parameters, and return types.
135
136
```typescript
137
import { test, expectTypeOf } from 'vitest';
138
139
test('function types', () => {
140
const add = (a: number, b: number): number => a + b;
141
142
// Check if callable
143
expectTypeOf(add).toBeCallableWith(1, 2);
144
145
// Check parameters
146
expectTypeOf(add).parameters.toEqualTypeOf<[number, number]>();
147
148
// Check return type
149
expectTypeOf(add).returns.toEqualTypeOf<number>();
150
});
151
152
test('constructor types', () => {
153
class User {
154
constructor(public name: string, public age: number) {}
155
}
156
157
expectTypeOf(User).toBeConstructibleWith('John', 30);
158
expectTypeOf(User).instance.toHaveProperty('name');
159
expectTypeOf(User).instance.toHaveProperty('age');
160
});
161
```
162
163
### Object Type Testing
164
165
Test object structure and properties.
166
167
```typescript
168
import { test, expectTypeOf } from 'vitest';
169
170
test('object types', () => {
171
type User = {
172
id: number;
173
name: string;
174
email?: string;
175
};
176
177
// Exact type match
178
expectTypeOf<User>().toEqualTypeOf<{
179
id: number;
180
name: string;
181
email?: string;
182
}>();
183
184
// Structural match (allows extra properties)
185
expectTypeOf<User>().toMatchTypeOf<{
186
id: number;
187
name: string;
188
}>();
189
190
// Property types
191
expectTypeOf<User>().toHaveProperty('id').toBeNumber();
192
expectTypeOf<User>().toHaveProperty('name').toBeString();
193
expectTypeOf<User>().toHaveProperty('email').toEqualTypeOf<string | undefined>();
194
});
195
```
196
197
### Generic Type Testing
198
199
Test generic types and type parameters.
200
201
```typescript
202
import { test, expectTypeOf } from 'vitest';
203
204
test('generic types', () => {
205
// Array item types
206
expectTypeOf<string[]>().items.toBeString();
207
expectTypeOf<number[]>().items.toBeNumber();
208
209
// Promise resolution types
210
expectTypeOf<Promise<string>>().resolves.toBeString();
211
expectTypeOf<Promise<number>>().resolves.toBeNumber();
212
213
// Generic function return types
214
function identity<T>(value: T): T {
215
return value;
216
}
217
218
expectTypeOf(identity<string>).returns.toBeString();
219
expectTypeOf(identity<number>).returns.toBeNumber();
220
});
221
```
222
223
### Union and Intersection Types
224
225
```typescript
226
import { test, expectTypeOf } from 'vitest';
227
228
test('union types', () => {
229
type StringOrNumber = string | number;
230
231
expectTypeOf<StringOrNumber>().toMatchTypeOf<string>();
232
expectTypeOf<StringOrNumber>().toMatchTypeOf<number>();
233
expectTypeOf<string>().toMatchTypeOf<StringOrNumber>();
234
});
235
236
test('intersection types', () => {
237
type Named = { name: string };
238
type Aged = { age: number };
239
type Person = Named & Aged;
240
241
expectTypeOf<Person>().toHaveProperty('name').toBeString();
242
expectTypeOf<Person>().toHaveProperty('age').toBeNumber();
243
expectTypeOf<Person>().toMatchTypeOf<Named>();
244
expectTypeOf<Person>().toMatchTypeOf<Aged>();
245
});
246
```
247
248
### AssertType
249
250
Runtime-free type assertions for checking type compatibility.
251
252
```typescript { .api }
253
/**
254
* Assert that value matches type T
255
* Causes TypeScript error if types don't match
256
* @param value - Value to type check
257
*/
258
function assertType<T>(value: T): void;
259
```
260
261
**Usage:**
262
263
```typescript
264
import { test, assertType } from 'vitest';
265
266
test('assert types', () => {
267
const value: string = 'hello';
268
269
assertType<string>(value); // OK
270
// assertType<number>(value); // TypeScript error
271
272
const obj = { id: 1, name: 'John' };
273
274
assertType<{ id: number; name: string }>(obj); // OK
275
276
const promise = Promise.resolve(42);
277
278
assertType<Promise<number>>(promise); // OK
279
});
280
```
281
282
### Complex Type Scenarios
283
284
```typescript
285
import { test, expectTypeOf } from 'vitest';
286
287
test('conditional types', () => {
288
type IsString<T> = T extends string ? true : false;
289
290
expectTypeOf<IsString<string>>().toEqualTypeOf<true>();
291
expectTypeOf<IsString<number>>().toEqualTypeOf<false>();
292
});
293
294
test('mapped types', () => {
295
type Readonly<T> = {
296
readonly [K in keyof T]: T[K];
297
};
298
299
type User = { name: string; age: number };
300
type ReadonlyUser = Readonly<User>;
301
302
expectTypeOf<ReadonlyUser>().toEqualTypeOf<{
303
readonly name: string;
304
readonly age: number;
305
}>();
306
});
307
308
test('utility types', () => {
309
type User = { id: number; name: string; email: string };
310
311
expectTypeOf<Partial<User>>().toEqualTypeOf<{
312
id?: number;
313
name?: string;
314
email?: string;
315
}>();
316
317
expectTypeOf<Required<User>>().toEqualTypeOf<{
318
id: number;
319
name: string;
320
email: string;
321
}>();
322
323
expectTypeOf<Pick<User, 'id' | 'name'>>().toEqualTypeOf<{
324
id: number;
325
name: string;
326
}>();
327
328
expectTypeOf<Omit<User, 'email'>>().toEqualTypeOf<{
329
id: number;
330
name: string;
331
}>();
332
});
333
```
334
335
## Typecheck Configuration
336
337
Configure type checking in vitest.config.ts:
338
339
```typescript
340
import { defineConfig } from 'vitest/config';
341
342
export default defineConfig({
343
test: {
344
typecheck: {
345
enabled: true,
346
checker: 'tsc', // or 'vue-tsc'
347
include: ['**/*.{test,spec}-d.ts'],
348
exclude: ['node_modules'],
349
tsconfig: './tsconfig.json'
350
}
351
}
352
});
353
```
354
355
## Running Type Tests
356
357
Type tests are checked during regular test runs, or can be run separately:
358
359
```bash
360
# Run all tests including type tests
361
vitest
362
363
# Run only type tests
364
vitest typecheck
365
```
366
367
## Common Patterns
368
369
### Testing API Return Types
370
371
```typescript
372
import { test, expectTypeOf } from 'vitest';
373
374
test('API types', () => {
375
async function fetchUser(id: number): Promise<{
376
id: number;
377
name: string;
378
email: string;
379
}> {
380
// implementation
381
}
382
383
expectTypeOf(fetchUser).parameter(0).toBeNumber();
384
expectTypeOf(fetchUser).returns.resolves.toMatchTypeOf<{
385
id: number;
386
name: string;
387
email: string;
388
}>();
389
});
390
```
391
392
### Testing Type Narrowing
393
394
```typescript
395
import { test, expectTypeOf, assertType } from 'vitest';
396
397
test('type narrowing', () => {
398
function process(value: string | number) {
399
if (typeof value === 'string') {
400
assertType<string>(value);
401
expectTypeOf(value).toBeString();
402
} else {
403
assertType<number>(value);
404
expectTypeOf(value).toBeNumber();
405
}
406
}
407
});
408
```
409
410
### Testing Generic Constraints
411
412
```typescript
413
import { test, expectTypeOf } from 'vitest';
414
415
test('generic constraints', () => {
416
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
417
return obj[key];
418
}
419
420
type User = { name: string; age: number };
421
const user: User = { name: 'John', age: 30 };
422
423
expectTypeOf(getProperty(user, 'name')).toBeString();
424
expectTypeOf(getProperty(user, 'age')).toBeNumber();
425
});
426
```
427
428
## Type Definitions
429
430
```typescript { .api }
431
type ExpectTypeOf<T> = {
432
toEqualTypeOf: <Expected>() => void;
433
toMatchTypeOf: <Expected>() => void;
434
toBeAny: () => void;
435
toBeUnknown: () => void;
436
toBeNever: () => void;
437
toBeFunction: () => void;
438
toBeObject: () => void;
439
toBeArray: () => void;
440
toBeString: () => void;
441
toBeNumber: () => void;
442
toBeBoolean: () => void;
443
toBeVoid: () => void;
444
toBeNull: () => void;
445
toBeUndefined: () => void;
446
toBeNullable: () => void;
447
toBeCallableWith: (...args: any[]) => void;
448
toBeConstructibleWith: (...args: any[]) => void;
449
toHaveProperty: <K extends keyof T>(key: K) => ExpectTypeOf<T[K]>;
450
not: ExpectTypeOf<T>;
451
resolves: T extends Promise<infer U> ? ExpectTypeOf<U> : never;
452
parameters: T extends (...args: infer P) => any ? ExpectTypeOf<P> : never;
453
returns: T extends (...args: any[]) => infer R ? ExpectTypeOf<R> : never;
454
items: T extends (infer Item)[] ? ExpectTypeOf<Item> : never;
455
};
456
457
type AssertType = <T>(value: T) => void;
458
```
459