Type Compiler
A TypeScript compiler plugin that automatically generates Zod schemas from TypeScript types for runtime validation. The plugin integrates directly with the TypeScript compiler (tsc) to provide runtime type checking through Zod.
Table of Contents
- Installation
- Getting Started
- Features
- Plugin Options
- Examples
- Performance
- Troubleshooting
- Documentation
- Development
- Testing & Stability
- Contributing
- License
Installation
npm install --save-dev type-compiler zod
Getting Started
This guide will help you quickly set up Type Compiler and understand its basic functionality.
1. Basic Setup
Add the plugin to your tsconfig.json
:
{
"compilerOptions": {
"plugins": [
{
"name": "type-compiler",
"generateZodSchemas": true
}
]
}
}
2. Create a Simple Interface
// user.ts
export interface User {
id: number;
name: string;
email: string;
}
3. Compile Your TypeScript Code
When you compile your code with TypeScript, the plugin automatically generates Zod schemas:
npx tsc
4. Use the Generated Schema
The plugin adds a Zod schema to your file with a 'z' prefix:
// In user.ts after compilation, you can use:
import { zUser } from './user';
// Get some data (from an API, user input, etc.)
const userData = fetchUserData();
// Validate at runtime
try {
const validUser = zUser.parse(userData);
console.log("Valid user:", validUser);
} catch (error) {
console.error("Invalid user data:", error);
}
5. Add Field-Specific Validation
For more specific validation requirements, configure special validators:
{
"compilerOptions": {
"plugins": [
{
"name": "type-compiler",
"generateZodSchemas": true,
"specialFieldValidators": {
"email": "z.string().email()",
"url": "z.string().url()"
}
}
]
}
}
Now fields named "email" will automatically use z.string().email()
validation.
6. Configure IDE Support
The plugin includes TypeScript Language Service integration that provides:
- Hover tooltips showing validation rules
- Code completion for validated fields
- Visual indicators for fields with special validation
No additional configuration is needed for IDE support beyond the tsconfig.json setup.
Features
- Automatic Zod Schema Generation: Converts TypeScript interfaces and type aliases to Zod schemas
- TypeScript Language Service Integration: Provides IDE hints, hover information, and code completion
- Pattern-Based Field Matching: Apply validators to fields matching regex patterns
- Contextual Validation: Apply different validation to fields based on parent type
- Class Method Validation: Generate validators for class constructors and methods
- Support for Advanced TypeScript Types:
- Generic types
- Mapped types (Partial, Pick, Omit, etc.)
- Union and intersection types
- Literal types and more
- Performance Optimizations:
- Global type cache
- Incremental compilation
- Parallel processing with worker threads
Configuration
Basic Configuration
Configure the plugin in your tsconfig.json
:
{
"compilerOptions": {
"plugins": [
{
"name": "type-compiler",
"generateZodSchemas": true,
"zodSchemaPrefix": "z",
"strictTypeChecking": true,
"validateClassMethods": true,
"specialFieldValidators": {
"email": {
"validator": "z.string().email()",
"errorMessage": "Please enter a valid email address"
},
"url": {
"validator": "z.string().url()",
"errorMessage": "Please enter a valid URL"
},
"phoneNumber": {
"validator": "z.string().regex(/^\\+?[1-9]\\d{1,14}$/)",
"errorMessage": "Phone number must be in international format (e.g. +12345678901)"
},
"^.*Email$": {
"pattern": true,
"validator": "z.string().email()",
"errorMessage": "Must be a valid email address"
},
"^id[A-Z]": {
"pattern": true,
"validator": "z.string().uuid()",
"errorMessage": "Must be a valid UUID"
}
},
"contextualValidators": {
"User": {
"email": {
"validator": "z.string().email().endsWith('@company.com')",
"errorMessage": "Company email must end with @company.com"
},
"role": {
"validator": "z.enum(['admin', 'user', 'guest'])",
"errorMessage": "Role must be one of: admin, user, or guest"
}
},
"Customer": {
"email": "z.string().email()",
"status": {
"validator": "z.enum(['active', 'inactive', 'pending'])",
"errorMessage": "Status must be either 'active', 'inactive', or 'pending'"
}
},
"^.*Product$": {
"pattern": true,
"fields": {
"price": {
"validator": "z.number().positive().min(0.01)",
"errorMessage": "Price must be greater than $0.01"
},
"inventory": {
"validator": "z.number().int().min(0)",
"errorMessage": "Inventory must be a non-negative integer"
}
}
}
}
}
]
}
}
TypeScript CLI Configuration
If you're using TypeScript CLI, specify the plugin via command line:
tsc --plugin type-compiler
Webpack Configuration
For webpack users:
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
compiler: 'typescript',
compilerOptions: {
plugins: [
{
transform: 'type-compiler',
generateZodSchemas: true
}
]
}
}
}
]
}
};
Plugin Options
Option | Type | Default | Description |
---|---|---|---|
generateZodSchemas |
boolean | false |
Enables automatic Zod schema generation for interfaces and type aliases |
zodSchemaPrefix |
string | "z" |
Prefix for generated Zod schema variables |
strictTypeChecking |
boolean | false |
Enables additional type checking rules and warnings |
validateClassMethods |
boolean | false |
Generates validators for class constructors and methods |
onlyExported |
boolean | false |
When true, only generates schemas for exported types |
includedTypes |
string[] | [] |
Specific type names to include even if they don't match other criteria |
excludedTypes |
string[] | [] |
Specific type names to exclude even if they match other criteria |
excludePatterns |
string[] | [] |
Glob patterns for files to exclude from processing |
useGlobalCache |
boolean | false |
When true, enables the global type cache for improved performance |
maxCacheSize |
number | 1000 |
Maximum number of entries to keep in the global type cache |
incrementalCompilation |
boolean | false |
When true, enables incremental compilation, only processing files that have changed |
incrementalCachePath |
string | - | Path to store the incremental compilation cache (if not provided, cache is kept in memory) |
parallelProcessing |
boolean | false |
When true, enables parallel processing of types for improved performance on multi-core systems |
workerCount |
number | CPU cores - 1 | Number of worker threads to use for parallel processing (0 = auto) |
workerBatchSize |
number | 100 |
Maximum number of types to process in a single worker batch |
specialFieldValidators |
object | {} |
Define custom validation rules for fields with specific names or matching patterns |
contextualValidators |
object | {} |
Type-specific validation rules that define different validations for the same field name in different contexts. Supports both exact type name matches and pattern-based matching |
Examples
Interface and Type Validation
// user.ts
interface User {
id: number;
name: string;
email: string;
age?: number;
roles: string[];
}
// Generated schema (added automatically by the plugin):
import { z } from 'zod';
export const zUser = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(), // Special validator applied
age: z.number().optional(),
roles: z.array(z.string())
});
Contextual Validation Example
// types.ts
interface User {
email: string; // Will use z.string().email().endsWith('@company.com')
role: string; // Will use z.enum(['admin', 'user', 'guest'])
}
interface Customer {
email: string; // Will use z.string().email()
status: string; // Will use z.enum(['active', 'inactive', 'pending'])
}
// Generated schemas (added automatically):
export const zUser = z.object({
email: z.string().email().endsWith('@company.com'),
role: z.enum(['admin', 'user', 'guest'])
});
export const zCustomer = z.object({
email: z.string().email(),
status: z.enum(['active', 'inactive', 'pending'])
});
Mapped Types Example
interface User {
id: number;
name: string;
email: string;
}
// Using TypeScript's utility types
type PartialUser = Partial<User>;
type UserBasicInfo = Pick<User, 'id' | 'name'>;
// The plugin generates:
export const zPartialUser = zUser.partial();
export const zUserBasicInfo = zUser.pick({
'id': true,
'name': true
});
Generic Types Example
interface Container<T> {
value: T;
metadata: {
timestamp: number;
};
}
// The plugin generates:
export const zContainer = <T extends z.ZodTypeAny>(valueSchema: T) =>
z.object({
value: valueSchema,
metadata: z.object({
timestamp: z.number()
})
});
// Usage:
const StringContainer = zContainer(z.string());
Custom Error Messages
Type Compiler allows you to define custom error messages for validation rules, providing more specific and helpful feedback to users:
// product.ts
interface Product {
id: string;
name: string;
price: number;
stockCount: number;
}
// With the following configuration in tsconfig.json:
// "specialFieldValidators": {
// "price": {
// "validator": "z.number().positive().min(0.01)",
// "errorMessage": "Price must be greater than $0.01"
// },
// "stockCount": {
// "validator": "z.number().int().min(0)",
// "errorMessage": "Stock count must be a non-negative integer"
// }
// }
// Usage:
import { zProduct } from './product';
try {
const product = zProduct.parse({
id: "123",
name: "Sample Product",
price: 0, // Invalid price
stockCount: -5 // Invalid stock count
});
} catch (error) {
console.error(error.errors);
// Will output customized error messages:
// [
// {
// "path": ["price"],
// "message": "Price must be greater than $0.01"
// },
// {
// "path": ["stockCount"],
// "message": "Stock count must be a non-negative integer"
// }
// ]
}
Benefits of custom error messages:
- Improved User Experience: Provides clear, context-specific guidance when validation fails
- Reduced Support Burden: Clear error messages lead to fewer support requests
- Consistency: Ensures consistent messaging across your application
- Domain-Specific Language: Use terminology that makes sense for your business domain
- Internationalization Support: Can be used with translation systems for multi-language applications
Configuring Custom Error Messages
Custom error messages can be configured at multiple levels:
Field-level validation:
"specialFieldValidators": { "email": { "validator": "z.string().email()", "errorMessage": "Please enter a valid email address" } }
Pattern-based validation:
"specialFieldValidators": { "^.*Email$": { "pattern": true, "validator": "z.string().email()", "errorMessage": "Must be a valid email address" } }
Contextual validation:
"contextualValidators": { "User": { "email": { "validator": "z.string().email().endsWith('@company.com')", "errorMessage": "Company email must end with @company.com" } } }
Performance
Type Compiler includes several optimizations to improve performance, especially for large codebases.
Performance Benchmarks
Here are typical performance improvements you can expect:
Project Size | Basic Compilation | With Global Cache | With Incremental | With Parallel (8 cores) |
---|---|---|---|---|
Small (<100 types) | 1.0s | 0.9s (10% faster) | 0.5s (50% faster) | 0.8s (20% faster) |
Medium (100-500 types) | 5.0s | 4.0s (20% faster) | 2.0s (60% faster) | 2.5s (50% faster) |
Large (500-1000 types) | 15.0s | 11.0s (27% faster) | 5.0s (67% faster) | 4.5s (70% faster) |
Very Large (1000+ types) | 45.0s | 32.0s (29% faster) | 15.0s (67% faster) | 12.0s (73% faster) |
Note: Actual performance will vary based on type complexity, hardware, and specific project characteristics.
Performance Optimization Features
Global Type Cache
Stores computed Zod schemas in memory to avoid redundant computation:
{
"compilerOptions": {
"plugins": [
{
"name": "type-compiler",
"useGlobalCache": true,
"maxCacheSize": 10000
}
]
}
}
When to use: Enable for all projects; especially beneficial for projects with many common or reused types.
Incremental Compilation
Only processes files that have changed since the last compilation:
{
"compilerOptions": {
"plugins": [
{
"name": "type-compiler",
"incrementalCompilation": true,
"incrementalCachePath": "./node_modules/.cache/type-compiler"
}
]
}
}
When to use: Enable for all projects during development; provides the most benefit for projects with many files where only a few change between compilations.
Parallel Processing
Utilizes worker threads to distribute processing across multiple CPU cores:
{
"compilerOptions": {
"plugins": [
{
"name": "type-compiler",
"parallelProcessing": true,
"workerCount": 4
}
]
}
}
When to use:
- Enable for medium to large projects (100+ types)
- Most effective on machines with 4+ CPU cores
- Consider disabling for small projects where the overhead may exceed the benefits
Optimization Recommendations
- Start Simple: Begin with just
useGlobalCache: true
for all projects - Iterative Improvement: Add
incrementalCompilation: true
if you find compilation times still slow - Complex Projects: Add
parallelProcessing: true
for large projects or complex type hierarchies - Fine-tuning: Adjust
workerCount
based on your specific hardware (general rule: CPU cores - 1)
Troubleshooting
Common Issues and Solutions
"Cannot find module 'zod'"
Problem: The compiler complains about missing the Zod module.
Solution: Ensure Zod is installed:
npm install zod
"The specified path does not exist: ... node_modules/type-compiler"
Problem: TypeScript can't find the plugin.
Solution: Verify your installation and make sure your path is correct in tsconfig.json:
npm install --save-dev type-compiler
Duplicate identifiers for generated schemas
Problem: You get errors about duplicate identifiers for your Zod schemas.
Solution: Make sure you're not manually defining schemas with the same names. If needed, customize the prefix:
{
"plugins": [
{
"name": "type-compiler",
"zodSchemaPrefix": "customPrefix"
}
]
}
Performance is slow with parallel processing
Problem: Enabling parallel processing doesn't improve performance or makes it worse.
Solution: Parallel processing has overhead. For small projects, disable it or adjust settings:
{
"plugins": [
{
"name": "type-compiler",
"parallelProcessing": true,
"workerCount": 2, // Try a smaller number
"workerBatchSize": 200 // Try a larger batch size to reduce overhead
}
]
}
Invalid regex patterns
Problem: Your pattern-based field validators aren't working as expected.
Solution: Validate your regex patterns and check for proper escaping in JSON:
"^id\\d+$": {
"pattern": true,
"validator": "z.string().uuid()"
}
Type errors in generated schemas
Problem: The generated schemas have TypeScript errors.
Solution: Check for circular type references or complex nested types. You can exclude problematic types:
{
"plugins": [
{
"name": "type-compiler",
"excludedTypes": ["ProblemType"]
}
]
}
IDE features not working
Problem: Hover tooltips or code completion for validation rules aren't showing.
Solution:
- Make sure your IDE is using the workspace version of TypeScript
- Restart your TypeScript server
- Verify your tsconfig.json has the plugin correctly configured
# In VS Code, you can restart the TS server with:
# Press Ctrl+Shift+P, then type "TypeScript: Restart TS Server"
Debugging Tips
For more advanced debugging:
Enable verbose logging:
{ "plugins": [ { "name": "type-compiler", "verbose": true } ] }
Check compiler output:
npx tsc --listEmittedFiles
Inspect generated code: Look at the compiled JavaScript files to see if the schemas are being generated correctly.
Isolate problematic types: Create a minimal reproduction case with just the types causing issues.
Documentation
For detailed guides and examples, check out our documentation:
- Special Field Validators - Configure custom Zod validators based on field names
- Contextual Validators - Type-specific validation rules
- Pattern Matching Visuals - Visual diagrams of how pattern matching works
- IDE Integration - How to use the IDE hints and autocompletion features
- Documentation Index - Navigate all documentation resources
Supported TypeScript Types
The plugin can convert the following TypeScript types to Zod schemas:
- Primitive types (string, number, boolean, null, undefined, bigint)
- Literal types (string literals, number literals, boolean literals)
- Arrays and tuples
- Objects and interfaces
- Union types
- Intersection types
- Optional properties
- Generic types and type parameters
- Record types
- Promise types
- Class methods and constructors
- Mapped types (Partial, Pick, Omit, Record, Readonly, etc.)
- Custom mapped types with property transformations
Development
# Install dependencies
npm install
# Build the project
npm run build
# Run tests
npm test
# Run linting
npm run lint
CI/CD Setup
This project uses GitHub Actions for continuous integration and deployment. The workflow automatically runs tests, builds the project, and publishes to npm when changes are merged to the main branch.
Setting Up CI/CD
Configure NPM Token: To allow GitHub Actions to publish to npm, you need to create an npm token and add it to your GitHub repository secrets:
Create an npm token:
- Go to npmjs.com
- Click on your profile icon and select "Access Tokens"
- Click "Generate New Token" and select "Publish"
- Copy the generated token
Add the token to GitHub secrets:
- Go to your GitHub repository
- Navigate to "Settings" > "Secrets and variables" > "Actions"
- Click "New repository secret"
- Name:
NPM_TOKEN
- Value: paste your npm token
- Click "Add secret"
Workflow File: The CI/CD workflow is defined in
.github/workflows/ci-cd.yml
and includes the following jobs:- Test: Runs all unit tests
- Build: Executes the bootstrap command to build the project
- Publish: Publishes the package to npm (only runs on the main branch)
Manual Triggering: You can manually trigger the workflow from the "Actions" tab in your GitHub repository by selecting the workflow and clicking "Run workflow".
Testing & Stability
Type Compiler maintains a comprehensive test suite to ensure functionality, compatibility, and stability across different environments and use cases.
Test Coverage
The project includes several layers of tests:
- Unit Tests: Test individual functions and components in isolation
- Integration Tests: Test interactions between different parts of the system
- End-to-End Tests: Validate the entire workflow from TypeScript types to generated schemas
- Edge Case Tests: Cover unusual or complex type scenarios
Key areas covered by the test suite include:
- Basic type conversions (primitives, objects, arrays)
- Complex TypeScript features (generics, mapped types, conditional types)
- Performance optimizations (caching, incremental compilation, parallel processing)
- Special field validators and pattern matching
- Contextual validators
- TypeScript Language Service plugin functionality
Running Tests
To run the complete test suite:
npm test
To run specific test categories:
# Run only unit tests
npm run test:unit
# Run only integration tests
npm run test:integration
# Run tests with coverage report
npm run test:coverage
Edge Case Testing
The test suite includes specialized tests for challenging edge cases:
- Circular References: Types that reference themselves directly or indirectly
- Deeply Nested Types: Complex hierarchies of types that test recursive processing
- Complex Generics: Generic types with multiple type parameters and constraints
- Mixed Types: Combinations of different type features like mapped types with generics
- Exotic TypeScript Features: Things like template literal types, infer keyword usage
- Large Scale Types: Performance testing with very large interfaces or numerous types
Integration Testing
Integration tests ensure that Type Compiler works correctly with:
- Different versions of TypeScript (from 4.0 to latest)
- Different versions of Zod
- Various build tools (webpack, Rollup, etc.)
- Real-world frameworks (React, Vue, etc.)
- Monorepos using different package managers
Contributing to Tests
When contributing new features or bug fixes, please include appropriate tests that:
- Verify the intended functionality
- Guard against regressions
- Document the expected behavior
Test files should follow the naming convention *.test.ts
and be placed in the src/__tests__
directory, mirroring the structure of the source files they test.
Example of a good test:
import { typeToZodSchema } from '../type-processor';
describe('typeToZodSchema', () => {
test('handles complex intersection types correctly', () => {
// Setup the complex type
const mockType = createMockIntersectionType([
createMockObjectType({ name: 'string', age: 'number' }),
createMockObjectType({ isActive: 'boolean' })
]);
// Execute the function
const result = typeToZodSchema(mockType, mockTypeChecker);
// Verify the output matches expected Zod schema
expect(result).toContain('z.object({ name: z.string(), age: z.number() })');
expect(result).toContain('z.object({ isActive: z.boolean() })');
expect(result).toContain('.and(');
});
});
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT