| license | Apache-2.0 |
| name | typescript-advanced-patterns |
| description | Advanced TypeScript type system patterns for production codebases. [What: branded types for nominal typing, discriminated unions, template literal types, conditional types, the infer keyword, satisfies operator, const assertions, Zod schema inference, type-safe event emitters, exhaustive switch checking] [When: designing domain models, building type-safe APIs, creating reusable generic utilities, eliminating runtime bugs with compile-time guarantees, refactoring any-typed codebases] [Keywords: branded types, discriminated union, template literal types, conditional types, infer, satisfies, const assertion, Zod inference, exhaustive, mapped types, utility types, nominal typing, type narrowing, generic constraints] NOT for basic TypeScript syntax or React component typing (use a React-specific skill). |
| allowed-tools | Read,Write,Edit,Bash(npm:*,npx:*,tsc:*) |
| argument-hint | [problem type: nominal-typing|discriminated-union|type-safe-events|zod-inference|conditional-types] |
| metadata | {"category":"Code Quality & Testing","pairs-with":[{"skill":"api-architect","reason":"Type-safe API contracts with Zod"},{"skill":"vitest-testing-patterns","reason":"Type-level testing with expect-type"},{"skill":"react-performance-optimizer","reason":"Type-safe React patterns"}],"tags":["typescript","type-system","branded-types","generics","zod"]} |
| category | Code Quality & Testing |
| tags | ["typescript","advanced-patterns","generics","type-system","best-practices"] |
TypeScript Advanced Patterns
Advanced type system patterns that eliminate runtime bugs by encoding constraints at compile time.
DECISION POINTS
Problem → Pattern Selection Tree
1. Are you mixing values of the same primitive type?
├─ YES: ID confusion (UserId vs OrderId) → Use Branded Types
├─ YES: Money confusion (USD vs EUR, dollars vs cents) → Use Branded Types with validation
└─ NO: Continue to #2
2. Do you have a value that can be one of N different shapes?
├─ YES: API responses (success/error/loading) → Use Discriminated Unions
├─ YES: State machine states → Use Discriminated Unions with exhaustive checking
└─ NO: Continue to #3
3. Are you parsing external data (APIs, user input)?
├─ YES: Unknown JSON shape → Use Zod schema + z.infer<typeof Schema>
├─ YES: Form validation → Use Zod with branded types for validated inputs
└─ NO: Continue to #4
4. Do you need types that compute based on other types?
├─ YES: Extract function parameters → Use conditional types with infer
├─ YES: Transform object shapes → Use mapped types with template literals
└─ NO: Continue to #5
5. Are you validating without losing specific type info?
├─ YES: Config objects with optional fields → Use satisfies operator
├─ YES: Const arrays that need narrow types → Use const assertions
└─ NO: Review if advanced patterns are needed
Implementation Strategy Decision
IF (primitive mixing bugs possible)
→ Start with branded types for domain IDs
→ Add Zod constructors for validation
IF (multiple related states)
→ Define discriminated union with 'kind'/'type'/'status' field
→ Add exhaustive switch with assertNever default
IF (external data + type safety needed)
→ Define Zod schema first
→ Export type as z.infer<typeof Schema>
→ Never manually write types for external data
IF (generic utilities needed)
→ Use conditional types with infer for extraction
→ Add constraints to prevent misuse
→ Test with expect-type for complex utilities
FAILURE MODES
1. Over-Branding Primitives
Symptom: Every string and number in codebase is branded
Detection: If you see Brand<string, 'FirstName'> and Brand<string, 'LastName'> that are never mixed up
Root Cause: Treating branding as general "make types stricter" instead of "prevent specific mixing bugs"
Fix: Only brand when there's actual confusion risk (IDs, money, different units)
2. Schema Bloat Anti-Pattern
Symptom: Zod schemas with 50+ fields, nested 5+ levels deep
Detection: Schema definitions longer than the components that use them
Root Cause: Trying to validate entire API response instead of just the fields you use
: Parse only what you need - instead of full user object