--- name: typescript-patterns description: TypeScript best practices and patterns. Use when writing TypeScript code, defining types, working with generics, or converting JavaScript to TypeScript. --- # TypeScript Patterns ## Strict Config (tsconfig.json) ```json { "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true, "noImplicitReturns": true, "exactOptionalPropertyTypes": true, "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "baseUrl": ".", "paths": { "@/*": ["src/*"] } } } ``` ## Type Patterns ### Discriminated Union (never use string + optional fields) ```typescript // BAD type ApiResponse = { success: boolean; data?: User; error?: string } // GOOD type ApiResponse = | { success: true; data: User } | { success: false; error: string } function handle(res: ApiResponse) { if (res.success) { console.log(res.data.email) // TypeScript knows data exists } else { console.error(res.error) // TypeScript knows error exists } } ``` ### Generic Repository ```typescript interface Repository { findById(id: ID): Promise findAll(): Promise create(data: Omit): Promise update(id: ID, data: Partial): Promise delete(id: ID): Promise } ``` ### Branded Types (prevent mixing IDs) ```typescript type UserId = number & { readonly _brand: 'UserId' } type PostId = number & { readonly _brand: 'PostId' } const userId = 123 as UserId const postId = 456 as PostId function getUser(id: UserId): Promise { ... } getUser(postId) // TypeScript error! Can't pass PostId as UserId ``` ### Utility Types ```typescript // Pick only what you need type UserSummary = Pick // Make all optional for updates type UserUpdate = Partial> // Require specific fields type UserCreate = Required> & Partial> // Readonly for immutable data type Config = Readonly<{ apiUrl: string; timeout: number }> // Record for maps const rolePermissions: Record = { ... } ``` ### Result Type (instead of throwing everywhere) ```typescript type Result = | { ok: true; value: T } | { ok: false; error: E } async function safeParseJson(text: string): Promise> { try { return { ok: true, value: JSON.parse(text) as T } } catch (e) { return { ok: false, error: e as Error } } } ``` ### Type Guards ```typescript function isUser(obj: unknown): obj is User { return typeof obj === 'object' && obj !== null && 'id' in obj && 'email' in obj && typeof (obj as User).email === 'string' } ``` ## Rules - Enable `strict: true` — never disable it for individual files - Never use `any` — use `unknown` and narrow with type guards - Prefer `interface` for object shapes, `type` for unions/intersections - Use discriminated unions over optional fields - Type return values of exported functions explicitly - Use `as const` for literal arrays/objects that shouldn't be widened - Avoid type assertions (`as X`) — use type guards instead - Prefer `readonly` properties for data that shouldn't change