Type Testing
Test TypeScript types without runtime execution.
Setup
Type tests use .test-d.ts extension:
// math.test-d.ts
import { expectTypeOf } from 'vitest'
import { add } from './math'
test('add returns number', () => {
expectTypeOf(add).returns.toBeNumber()
})Configuration
defineConfig({
test: {
typecheck: {
enabled: true,
// Only type check
only: false,
// Checker: 'tsc' or 'vue-tsc'
checker: 'tsc',
// Include patterns
include: ['**/*.test-d.ts'],
// tsconfig to use
tsconfig: './tsconfig.json',
},
},
})expectTypeOf API
import { expectTypeOf } from 'vitest'
// Basic type checks
expectTypeOf<string>().toBeString()
expectTypeOf<number>().toBeNumber()
expectTypeOf<boolean>().toBeBoolean()
expectTypeOf<null>().toBeNull()
expectTypeOf<undefined>().toBeUndefined()
expectTypeOf<void>().toBeVoid()
expectTypeOf<never>().toBeNever()
expectTypeOf<any>().toBeAny()
expectTypeOf<unknown>().toBeUnknown()
expectTypeOf<object>().toBeObject()
expectTypeOf<Function>().toBeFunction()
expectTypeOf<[]>().toBeArray()
expectTypeOf<symbol>().toBeSymbol()Value Type Checking
const value = 'hello'
expectTypeOf(value).toBeString()
const obj = { name: 'test', count: 42 }
expectTypeOf(obj).toMatchTypeOf<{ name: string }>()
expectTypeOf(obj).toHaveProperty('name')Function Types
function greet(name: string): string {
return `Hello, ${name}`
}
expectTypeOf(greet).toBeFunction()
expectTypeOf(greet).parameters.toEqualTypeOf<[string]>()
expectTypeOf(greet).returns.toBeString()
// Parameter checking
expectTypeOf(greet).parameter(0).toBeString()Object Types
interface User {
id: number
name: string
email?: string
}
expectTypeOf<User>().toHaveProperty('id')
expectTypeOf<User>().toHaveProperty('name').toBeString()
// Check shape
expectTypeOf({ id: 1, name: 'test' }).toMatchTypeOf<User>()Equality vs Matching
interface A { x: number }
interface B { x: number; y: string }
// toMatchTypeOf - subset matching
expectTypeOf<B>().toMatchTypeOf<A>() // B extends A
// toEqualTypeOf - exact match
expectTypeOf<A>().not.toEqualTypeOf<B>() // Not exact match
expectTypeOf<A>().toEqualTypeOf<{ x: number }>() // Exact matchBranded Types
type UserId = number & { __brand: 'UserId' }
type PostId = number & { __brand: 'PostId' }
expectTypeOf<UserId>().not.toEqualTypeOf<PostId>()
expectTypeOf<UserId>().not.toEqualTypeOf<number>()Generic Types
function identity<T>(value: T): T {
return value
}
expectTypeOf(identity<string>).returns.toBeString()
expectTypeOf(identity<number>).returns.toBeNumber()Nullable Types
type MaybeString = string | null | undefined
expectTypeOf<MaybeString>().toBeNullable()
expectTypeOf<string>().not.toBeNullable()assertType
Assert a value matches a type (no assertion at runtime):
import { assertType } from 'vitest'
function getUser(): User | null {
return { id: 1, name: 'test' }
}
test('returns user', () => {
const result = getUser()
// @ts-expect-error - should fail type check
assertType<string>(result)
// Correct type
assertType<User | null>(result)
})Using @ts-expect-error
Test that code produces type error:
test('rejects wrong types', () => {
function requireString(s: string) {}
// @ts-expect-error - number not assignable to string
requireString(123)
})Running Type Tests
# Run type tests
vitest typecheck
# Run alongside unit tests
vitest --typecheck
# Type tests only
vitest --typecheck.onlyMixed Test Files
Combine runtime and type tests:
// user.test.ts
import { describe, expect, expectTypeOf, test } from 'vitest'
import { createUser } from './user'
describe('createUser', () => {
test('runtime: creates user', () => {
const user = createUser('John')
expect(user.name).toBe('John')
})
test('types: returns User type', () => {
expectTypeOf(createUser).returns.toMatchTypeOf<{ name: string }>()
})
})Key Points
- Use
.test-d.tsfor type-only tests expectTypeOffor type assertionstoMatchTypeOffor subset matchingtoEqualTypeOffor exact type matching- Use
@ts-expect-errorto test type errors - Run with
vitest typecheckor--typecheck
<!-- Source references:
- https://vitest.dev/guide/testing-types.html
- https://vitest.dev/api/expect-typeof.html
-->