| name | typescript-best-practices |
| description | This skill should be used when the user asks to "review TypeScript code", "check my TS code", "review this TypeScript", "write TypeScript", or when writing, reviewing, or refactoring TypeScript code in projects with tsconfig.json or .ts/.tsx files. Provides dos and don'ts for type design, naming conventions, generics, and patterns. |
TypeScript Best Practices
Guidelines for writing clean, type-safe, and maintainable TypeScript code.
Note: If the repository has established code style conventions, follow those first. These guidelines serve as defaults.
Core Principles
- Type-First Design - Define types before implementation; minimize reliance on inference
- Interface for Structure - Use
interface for objects, type for unions/mapped/conditional
- Namespace for Type Organization - Group related types with namespaces (types only, not runtime)
- Generic Const for Strictness - Use
<const TConfig> for strict literal inference
- Extract, Don't Redefine - Get types from existing definitions instead of duplicating
- Strictest Config - Use strictest tsconfig base; install
ts-reset for saner built-in types
Quick Reference
interface vs type
| Use | When |
|---|
interface | Object structures, class contracts, extensible APIs |
type | Union types, mapped types, conditional types, tuples |
Naming Conventions
| Element | Convention | Example |
|---|
| Interface/Type | PascalCase | UserProfile, ResponseData |
| Generic parameters | T prefix | TUser, TConfig (never bare T, K, V) |
| Acronyms | First cap only | userId, ApiResponse (NOT userID, APIResponse) |
| Constants | UPPER_SNAKE | MAX_RETRY_COUNT |
| Variables/Functions | camelCase | getUserById, isActive |
Array Syntax
| DO | DON'T |
|---|
Array<TItem> | TItem[] |
ReadonlyArray<TItem> | readonly TItem[] |
Object Types
| Use Case | DO | DON'T |
|---|
| Empty object | Record<string, never> | {} |
| Any object (extends) | Record<string, any> | Record<string, unknown> |
| Any object (annotation) | Record<string, unknown> | Record<string, any> |
| Non-primitive | object | {} |
Assertions
| DO | DON'T |
|---|
| Zod/arktype for runtime validation | response as User |
satisfies for compile-time checks | value as unknown as Type |
Type guards (if ('prop' in obj)) | as any to silence errors |
| Explicit null checks | x! non-null assertion |
Function Declarations
const myFunction: myFunction.Type = (options) => {
};
const onClick = ((event) => {
}) satisfies React.ComponentProps<'button'>['onClick'];
Type Extraction
type OnClick = React.ComponentProps<'button'>['onClick'];
type ItemIds = Array<Item['id']>;
type TimeoutType = NonNullable<typeof config['timeout']>;
type BadItemIds = Array<number>;
Summary Checklist
Before committing TypeScript code, verify:
Reference Files
For detailed patterns and examples, see:
- type-patterns.md - Type syntax, assertions, namespace pattern, generics
- code-style.md - Safe array access, early return, avoid destructuring, avoid enum
- union-exhaustive.md - Discriminated unions + exhaustive handling (e.g., for state, events, API responses)
- branded-types.md - Nominal types for ID/unit safety (e.g., UserId vs OrderId)
- template-literals.md - String pattern types (e.g., event names, CSS values, route parameters)
- type-testing.md - Type-level testing with
*.test-d.ts files
- setup.md - tsconfig, strict options, ts-reset configuration
Notes
- These guidelines complement, not replace, project-specific conventions
- When in doubt, prioritize readability and maintainability
- Runtime type validation (zod, arktype) is recommended for external data
- Avoid over-engineering types; simple is better than clever