TypeScript best practices and common patterns
View on GitHubplugins/aai-stack-typescript/skills/typescript-patterns/SKILL.md
February 1, 2026
Select agents to install to:
npx add-skill https://github.com/the-answerai/alphaagent-team/blob/main/plugins/aai-stack-typescript/skills/typescript-patterns/SKILL.md -a claude-code --skill typescript-patternsInstallation paths:
.claude/skills/typescript-patterns/# TypeScript Patterns Skill
Best practices and patterns for writing type-safe TypeScript code.
## Type Definitions
### Interfaces vs Types
```typescript
// Use interfaces for object shapes (can be extended)
interface User {
id: string
name: string
email: string
}
interface AdminUser extends User {
permissions: string[]
}
// Use types for unions, intersections, and complex types
type Status = 'pending' | 'active' | 'inactive'
type Nullable<T> = T | null
type UserOrAdmin = User | AdminUser
```
### Readonly and Immutability
```typescript
// Readonly properties
interface Config {
readonly apiUrl: string
readonly timeout: number
}
// Readonly arrays
const items: readonly string[] = ['a', 'b', 'c']
// items.push('d') // Error!
// Deep readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
}
const config: DeepReadonly<Config> = {
apiUrl: 'https://api.example.com',
timeout: 5000,
}
```
### Optional vs Required
```typescript
interface UserInput {
name: string // Required
email: string // Required
phone?: string // Optional
age?: number // Optional
}
// Make all properties required
type RequiredUser = Required<UserInput>
// Make all properties optional
type PartialUser = Partial<UserInput>
// Pick specific properties
type UserContact = Pick<UserInput, 'email' | 'phone'>
// Omit specific properties
type UserWithoutAge = Omit<UserInput, 'age'>
```
## Discriminated Unions
### Pattern Matching with Unions
```typescript
interface LoadingState {
status: 'loading'
}
interface SuccessState {
status: 'success'
data: unknown
}
interface ErrorState {
status: 'error'
error: Error
}
type AsyncState = LoadingState | SuccessState | ErrorState
function handleState(state: AsyncState) {
switch (state.status) {
case 'loading':
return 'Loading...'
case 'success':
return `Data: ${JSON.stringify(state.data)}`
cas