UNPKG

type-compiler

Version:

A TypeScript compiler plugin for enhanced runtime type checking and analysis with Zod validation

453 lines (352 loc) 11.1 kB
# Special Field Validators The type-compiler plugin allows you to specify custom Zod validators for specific field names across your codebase. This is useful for ensuring consistent validation rules for common field types like emails, dates, and more. ## Configuration Configure special field validators in your `tsconfig.json`: ```json { "compilerOptions": { "plugins": [ { "name": "type-compiler", "generateZodSchemas": true, "specialFieldValidators": { "email": "z.string().email()", "birthDate": "z.string().pipe(z.coerce.date())", "url": "z.string().url()", "phoneNumber": "z.string().regex(/^\\+?[1-9]\\d{1,14}$/)" } } ] } } ``` ## How It Works When the type-compiler generates Zod schemas for your TypeScript types, it checks each property name against your configured `specialFieldValidators`. If a match is found, it uses your custom validator instead of the default one. ### Example Given this TypeScript interface: ```typescript interface User { id: number; email: string; birthDate: string; url: string; phoneNumber: string; name: string; // Regular field with no special validator } ``` With the configuration above, the generated Zod schema would be: ```typescript export const zUser = z.object({ id: z.number(), email: z.string().email(), birthDate: z.string().pipe(z.coerce.date()), url: z.string().url(), phoneNumber: z.string().regex(/^\+?[1-9]\d{1,14}$/), name: z.string() }); ``` ## Common Validators Here are some useful special field validators for common scenarios: ### Email Addresses ```json "email": "z.string().email()" ``` ### Dates ```json "birthDate": "z.string().pipe(z.coerce.date())", "createdAt": "z.date()", "updatedAt": "z.date()" ``` ### URLs and URIs ```json "url": "z.string().url()", "website": "z.string().url()", "imageUrl": "z.string().url()" ``` ### Phone Numbers ```json "phoneNumber": "z.string().regex(/^\\+?[1-9]\\d{1,14}$/)", "phone": "z.string().regex(/^\\+?[1-9]\\d{1,14}$/)" ``` ### Credit Cards ```json "creditCard": "z.string().regex(/^\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}$/)" ``` ### Geographic Coordinates ```json "latitude": "z.number().min(-90).max(90)", "longitude": "z.number().min(-180).max(180)" ``` ### UUIDs ```json "uuid": "z.string().uuid()", "id": "z.string().uuid()" ``` ### IP Addresses ```json "ipAddress": "z.string().ip()", "ipv4": "z.string().ip({ version: 'v4' })", "ipv6": "z.string().ip({ version: 'v6' })" ``` ## Advanced Usage ### Pattern Matching You can use the same validator for multiple similar field names: ```json { "specialFieldValidators": { "email": "z.string().email()", "userEmail": "z.string().email()", "contactEmail": "z.string().email()" } } ``` ### Complex Validators You can use more complex validation chains: ```json { "specialFieldValidators": { "password": "z.string().min(8).regex(/[A-Z]/).regex(/[a-z]/).regex(/[0-9]/).regex(/[^A-Za-z0-9]/)" } } ``` ### Transformations You can apply transformations as part of the validation: ```json { "specialFieldValidators": { "username": "z.string().toLowerCase().trim().min(3)" } } ``` ### Optional Fields Special validators work with optional fields too. The optionality is preserved: ```json { "specialFieldValidators": { "website": "z.string().url().optional()" } } ``` ## Best Practices 1. **Consistency**: Define validators once in your config to ensure consistent validation rules across your application. 2. **Documentation**: Keep your special field validators documented for team reference. 3. **Testing**: Test your validators with both valid and invalid data to ensure they behave as expected. 4. **Maintainability**: Group related validators together and use consistent naming patterns. 5. **Performance**: Avoid overly complex regex patterns that might impact performance. ## Limitations - Validators are applied based on exact field name matches. - Field validators don't consider the actual TypeScript type of the field. - Circular references in validators won't work. ## Pattern-Based Field Matching The type-compiler now supports regex pattern matching for field names, allowing you to apply validators more flexibly. This is especially useful when you have naming conventions or want to validate fields with similar purposes. ### Configuration To use pattern-based field matching, specify an object with `pattern: true` and your validator: ```json { "compilerOptions": { "plugins": [ { "name": "type-compiler", "specialFieldValidators": { "email": "z.string().email()", "^.*Email$": { "pattern": true, "validator": "z.string().email()" }, "^id": { "pattern": true, "validator": "z.string().uuid()" } } } ] } } ``` ### How Pattern Matching Works 1. For each field, the plugin first checks for exact matches in your `specialFieldValidators` configuration. 2. If no exact match is found, it checks all pattern-based validators to find a match. 3. If multiple patterns match, the first one defined in the configuration is used. 4. Exact matches always take precedence over pattern matches. ### Real-World Pattern Examples Here's a comprehensive list of useful pattern-based validators for common scenarios: #### Email Addresses and Contact Information ```json "^.*Email$": { "pattern": true, "validator": "z.string().email()" }, "^email[A-Z]": { "pattern": true, "validator": "z.string().email()" }, "^contact[A-Z]": { "pattern": true, "validator": "z.string()" } ``` #### Identifiers and References ```json ".*(?:Id|Key|Code)$": { "pattern": true, "validator": "z.string().min(1)" }, "^uuid$|^guid$": { "pattern": true, "validator": "z.string().uuid()" } ``` #### Timestamps and Dates ```json "^.*(?:At|Date|Time)$": { "pattern": true, "validator": "z.date().or(z.string().pipe(z.coerce.date()))" }, "^created|^updated|^modified": { "pattern": true, "validator": "z.date()" } ``` #### Monetary Values ```json "(?:amount|cost|price|fee|total)(?:$|[A-Z])": { "pattern": true, "validator": "z.number().min(0).or(z.string().regex(/^\\d+(\\.\\d{1,2})?$/).transform(Number))" }, "^currency$|^.*Currency$": { "pattern": true, "validator": "z.string().length(3).regex(/^[A-Z]{3}$/)" } ``` #### Status Fields and Enums ```json ".*Status$": { "pattern": true, "validator": "z.enum(['active', 'inactive', 'pending', 'completed', 'failed', 'cancelled']).or(z.string())" }, ".*Type$": { "pattern": true, "validator": "z.string()" } ``` #### Percentages and Rates ```json ".*(?:Percent|Rate|Ratio)$": { "pattern": true, "validator": "z.number().min(0).max(100)" }, ".*Decimal$": { "pattern": true, "validator": "z.number().min(0).max(1)" } ``` #### Counting Fields ```json ".*Count$": { "pattern": true, "validator": "z.number().int().min(0)" }, ".*Limit$": { "pattern": true, "validator": "z.number().int().positive()" } ``` #### Dimension Measurements ```json "(?:width|height|depth|length|radius|size)(?:$|[A-Z])": { "pattern": true, "validator": "z.number().positive()" }, ".*Size$": { "pattern": true, "validator": "z.number().positive()" } ``` #### Boolean Flags ```json "^is[A-Z]|^has[A-Z]|^can[A-Z]|^should[A-Z]": { "pattern": true, "validator": "z.boolean()" }, ".*Enabled$|.*Active$|.*Visible$": { "pattern": true, "validator": "z.boolean()" } ``` #### Collections and Arrays ```json "^(?:tags|categories|items|products|users)$": { "pattern": true, "validator": "z.array(z.any())" }, ".*List$|.*Array$|.*Collection$": { "pattern": true, "validator": "z.array(z.any())" } ``` #### Colors and UI Properties ```json ".*[Cc]olor$": { "pattern": true, "validator": "z.string().regex(/^#([0-9A-F]{3}){1,2}$/i).or(z.string().regex(/^rgb\\(\\d{1,3},\\s*\\d{1,3},\\s*\\d{1,3}\\)$/))" }, ".*Theme$": { "pattern": true, "validator": "z.string()" } ``` ### Real-World Use Cases #### E-commerce Product Data For an e-commerce system, pattern-based validators ensure consistent validation of product attributes: ```typescript interface Product { productId: string; // Matches identifier pattern name: string; basePrice: number; // Matches monetary pattern discountAmount: number; // Matches monetary pattern stockCount: number; // Matches count pattern isAvailable: boolean; // Matches boolean pattern primaryColor: string; // Matches color pattern productStatus: string; // Matches status pattern dimensions: { width: number; // Matches dimension pattern height: number; // Matches dimension pattern depth: number; // Matches dimension pattern }; createdAt: Date; // Matches timestamp pattern } ``` #### Financial Transactions Financial systems benefit from consistent validation of monetary values and rates: ```typescript interface Transaction { transactionId: string; // Matches identifier pattern amount: number; // Matches monetary pattern fee: number; // Matches monetary pattern interestRate: number; // Matches percentage pattern exchangeRate: number; // Matches rate pattern transactionDate: Date; // Matches timestamp pattern transactionStatus: string; // Matches status pattern } ``` #### Analytics Data Analytics systems use pattern matching to validate metrics and counts: ```typescript interface AnalyticsData { visitorCount: number; // Matches count pattern bounceRate: number; // Matches percentage pattern conversionRate: number; // Matches percentage pattern isRealTime: boolean; // Matches boolean pattern recordedAt: Date; // Matches timestamp pattern } ``` ### Best Practices for Pattern Matching 1. **Start with Specific Patterns**: Begin with more specific patterns and move to more general ones to avoid unexpected matches. 2. **Test Your Patterns**: Verify that your patterns match exactly the fields you expect. 3. **Use Anchors**: Use `^` and `$` anchors to ensure you're matching complete field names, not just substrings. 4. **Document Patterns**: Comment your regex patterns to explain what they're matching. 5. **Use Consistent Naming Conventions**: Adopt consistent naming conventions in your codebase to make patterns more effective. 6. **Provide Fallbacks**: For enums and specific formats, consider using `.or(z.string())` to provide a fallback when strict validation isn't required. 7. **Combine with Type Information**: Consider the expected TypeScript type when designing pattern validators. ## Example Project Check out the `examples/special-validators` directory for a complete example project demonstrating how to use special field validators effectively.