Comprehensive TypeScript support including type validation and test execution. Provides seamless integration for TypeScript projects with full type safety and validation capabilities.
Run TypeScript test files directly without manual compilation, with full type checking integration.
CLI Usage:
# Run TypeScript tests
lab --typescript
# Run with custom tsconfig
lab --typescript --require 'tsconfig-paths/register'
# Validate types during execution
lab --typescript --typesProgrammatic Usage:
const result = await Lab.report(script, {
typescript: true,
types: true,
'types-test': './test/types.ts'
});Validates TypeScript type definitions to ensure API correctness and catch type-related issues.
/**
* Validates TypeScript types and definitions
* @param options - Type validation configuration
* @returns Promise resolving to type validation report
*/
types.validate(options?: TypeOptions): Promise<TypesReport>;Usage Examples:
const Lab = require('@hapi/lab');
// Basic type validation
const typeReport = await Lab.types.validate();
// Validation with specific test file
const report = await Lab.types.validate({
'types-test': './test/api-types.ts'
});Provides utilities for asserting types in tests, ensuring compile-time and runtime type safety.
interface TypeExpect {
/**
* Assert the type of a value matches expected type T
* @param value - Value to type-check
*/
type<T>(value: T): void;
/**
* Assert that a value should produce a type error
* @param value - Value that should cause type error
*/
error<T = any>(value: T): void;
}
// Available as Lab.types.expect
const expect: TypeExpect;Usage Examples:
import * as Lab from '@hapi/lab';
import { expect as codeExpect } from '@hapi/code';
const lab = Lab.script();
const { describe, it } = lab;
export { lab };
describe('Type assertions', () => {
it('validates correct types', () => {
const name: string = 'test';
const age: number = 25;
// Assert types are correct
Lab.types.expect.type<string>(name);
Lab.types.expect.type<number>(age);
});
it('detects type errors', () => {
// This should produce a type error
Lab.types.expect.error<string>(123);
// This should also produce a type error
Lab.types.expect.error<number>('not a number');
});
});Configuration options for TypeScript type validation and testing.
interface TypeOptions {
/** Enable TypeScript type validation */
types?: boolean;
/** Path to TypeScript definitions test file */
'types-test'?: string;
/** TypeScript compiler options */
typescript?: boolean;
/** Custom TypeScript configuration file path */
tsconfig?: string;
}Comprehensive TypeScript integration features:
Detailed results from TypeScript type validation and compilation.
interface TypesReport {
/** Whether type validation passed */
passed: boolean;
/** Array of type errors found */
errors: TypeValidationError[];
/** Number of files validated */
filesValidated: number;
/** Validation execution time in milliseconds */
duration: number;
/** TypeScript compiler version used */
compilerVersion: string;
/** Configuration file used */
configFile?: string;
}
interface TypeValidationError {
/** Error message from TypeScript compiler */
message: string;
/** File where error occurred */
file: string;
/** Line number of error */
line: number;
/** Column number of error */
column: number;
/** Error code from TypeScript */
code: number;
/** Severity level of error */
severity: 'error' | 'warning';
/** Source code context around error */
context?: string;
}Standard TypeScript test file structure with full type safety:
import * as Lab from '@hapi/lab';
import { expect } from '@hapi/code';
// API being tested
import { UserService, User, CreateUserRequest } from '../src/user-service';
const lab = Lab.script();
const { describe, it, beforeEach, afterEach } = lab;
export { lab };
describe('UserService', () => {
let userService: UserService;
beforeEach(() => {
userService = new UserService();
});
it('creates user with correct types', async () => {
const request: CreateUserRequest = {
name: 'John Doe',
email: 'john@example.com',
age: 30
};
const user: User = await userService.createUser(request);
expect(user.id).to.be.a.string();
expect(user.name).to.equal(request.name);
expect(user.email).to.equal(request.email);
expect(user.age).to.equal(request.age);
expect(user.createdAt).to.be.a.date();
// Type assertions
Lab.types.expect.type<User>(user);
Lab.types.expect.type<string>(user.id);
Lab.types.expect.type<Date>(user.createdAt);
});
it('handles type errors correctly', () => {
// This should cause a type error
Lab.types.expect.error<CreateUserRequest>({
name: 'John',
email: 'invalid',
age: 'not a number' // Type error: age should be number
});
});
});Testing functions with generic types while preserving type information:
import * as Lab from '@hapi/lab';
import { expect } from '@hapi/code';
import { Repository } from '../src/repository';
const lab = Lab.script();
const { describe, it } = lab;
export { lab };
interface TestEntity {
id: string;
name: string;
}
describe('Generic Repository', () => {
it('preserves generic types', async () => {
const repo = new Repository<TestEntity>();
const entity: TestEntity = {
id: '123',
name: 'Test Entity'
};
const saved = await repo.save(entity);
const found = await repo.findById('123');
// Type assertions verify generic type preservation
Lab.types.expect.type<TestEntity>(saved);
Lab.types.expect.type<TestEntity | null>(found);
if (found) {
Lab.types.expect.type<TestEntity>(found);
expect(found.id).to.equal(entity.id);
expect(found.name).to.equal(entity.name);
}
});
});Testing TypeScript definition files to ensure API correctness:
// test/api-types.ts - Type definition tests
import * as Lab from '@hapi/lab';
// Import types being tested
import {
UserService,
User,
CreateUserRequest,
UpdateUserRequest,
UserRepository
} from '../src/index';
const { expect } = Lab.types;
// Test interface structure
expect.type<{
id: string;
name: string;
email: string;
age: number;
createdAt: Date;
updatedAt: Date;
}>(undefined as any as User);
// Test request interfaces
expect.type<{
name: string;
email: string;
age: number;
}>(undefined as any as CreateUserRequest);
expect.type<{
name?: string;
email?: string;
age?: number;
}>(undefined as any as UpdateUserRequest);
// Test service interface
expect.type<{
createUser(request: CreateUserRequest): Promise<User>;
updateUser(id: string, request: UpdateUserRequest): Promise<User>;
deleteUser(id: string): Promise<void>;
findById(id: string): Promise<User | null>;
findAll(): Promise<User[]>;
}>(undefined as any as UserService);
// Test error cases
expect.error<CreateUserRequest>({
name: 'John',
email: 'john@example.com'
// Missing required 'age' property
});
expect.error<User>({
id: '123',
name: 'John',
email: 'john@example.com',
age: 30,
createdAt: new Date()
// Missing required 'updatedAt' property
});Support for TypeScript path mapping and module resolution:
# Install path resolution support
npm install --save-dev tsconfig-paths
# Run tests with path mapping
lab --typescript --require 'tsconfig-paths/register'// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/models/*": ["src/models/*"],
"@/services/*": ["src/services/*"],
"@/utils/*": ["src/utils/*"]
}
}
}// Use path mapping in tests
import { User } from '@/models/user';
import { UserService } from '@/services/user-service';
import { validateEmail } from '@/utils/validation';Accurate stack traces and debugging with source map support:
// Enable source maps for better debugging
const result = await Lab.report(script, {
typescript: true,
sourcemaps: true
});Override TypeScript compiler options for specific test scenarios:
// Custom compiler configuration for tests
const compilerOptions = {
strict: true,
noImplicitAny: true,
noImplicitReturns: true,
noUnusedLocals: true,
noUnusedParameters: true
};Full example of a TypeScript project test configuration:
// test/setup.ts
import * as Lab from '@hapi/lab';
import { expect } from '@hapi/code';
// Global test configuration
const lab = Lab.script();
export { lab };
// Export commonly used test utilities
export { expect };
export const { describe, it, before, after, beforeEach, afterEach } = lab;
// Type assertion helpers
export const expectType = Lab.types.expect.type;
export const expectError = Lab.types.expect.error;// test/user-service.test.ts
import { lab, describe, it, expect, expectType } from './setup';
import { UserService, User } from '../src/user-service';
describe('UserService with full TypeScript support', () => {
it('creates users with type safety', async () => {
const service = new UserService();
const user = await service.createUser({
name: 'John Doe',
email: 'john@example.com',
age: 30
});
// Runtime assertions
expect(user.id).to.be.a.string();
expect(user.name).to.equal('John Doe');
// Type assertions
expectType<User>(user);
expectType<string>(user.id);
expectType<Date>(user.createdAt);
});
});
export { lab };Complete CI/CD setup with TypeScript validation:
// scripts/test-typescript.js
const Lab = require('@hapi/lab');
const runTypeScriptTests = async () => {
const result = await Lab.run({
typescript: true,
types: true,
'types-test': './test/api-types.ts',
coverage: true,
threshold: 90,
reporter: ['console', 'json'],
output: ['', 'test-results.json']
});
if (result.types && !result.types.passed) {
console.error('TypeScript validation failed:');
result.types.errors.forEach(error => {
console.error(` ${error.file}:${error.line}:${error.column} - ${error.message}`);
});
process.exit(1);
}
if (result.failures > 0) {
console.error(`${result.failures} test failures`);
process.exit(1);
}
console.log('All TypeScript tests passed!');
};
runTypeScriptTests();