typescript-best-practices
Original:🇺🇸 English
Translated
TypeScript best practices and patterns for writing type-safe, maintainable code. Use when working with TypeScript files, configuring tsconfig, defining interfaces/types, implementing error handling, writing generics, or setting up type-safe communication patterns. Includes patterns for discriminated unions, type guards, utility types, and more.
3installs
Sourceghosttypes/ff-5mp-api-ts
Added on
NPX Install
npx skill4agent add ghosttypes/ff-5mp-api-ts typescript-best-practicesTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →TypeScript Best Practices
Quick Start
Always enable strict mode and use explicit types for public APIs. Prefer type-only imports () and named exports over default exports. Use discriminated unions for state management and type guards for runtime validation.
import typeType Safety Fundamentals
Strict Mode Configuration
Enable all strict flags in :
tsconfig.jsonjson
{
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}Null Safety
typescript
// Explicit null handling
export function findById<T>(items: Map<string, T>, id: string): T | null {
return items.get(id) ?? null;
}
// Optional chaining and nullish coalescing
export function getName(item?: Item): string {
return item?.name ?? 'Unknown';
}Explicit Return Types
Use explicit return types for public APIs:
typescript
// Good
export function validateConfig(data: unknown): ValidatedConfig | null {
const result = ConfigSchema.safeParse(data);
return result.success ? result.data : null;
}
// Bad - implicit return type
export function validateConfig(data) {
return data;
}Core Patterns
Discriminated Unions for State
typescript
export type ConnectionState =
| { status: 'disconnected' }
| { status: 'connecting'; progress: number }
| { status: 'connected'; connectionId: string }
| { status: 'error'; message: string };
// TypeScript knows which properties are available in each branchResult Type for Error Handling
typescript
export type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
const result = await connectToService(options);
if (result.success) {
console.log('Connected:', result.data.id);
} else {
console.error('Failed:', result.error.message);
}Readonly Properties
Use for immutable data:
readonlytypescript
export interface Config {
readonly enableFeature: boolean;
readonly port: number;
}Type Guards and Assertions
typescript
// Type guard
export function isUserData(data: unknown): data is UserData {
return typeof data === 'object' && data !== null && 'name' in data;
}
// Assertion function
export function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
if (value === undefined || value === null) throw new Error('Value is undefined or null');
}Import/Export Conventions
typescript
// Type-only imports (preferred)
import type { Config } from '../types/config';
// Named exports (avoid default exports)
export class DataService {}
export const getService = () => DataService.getInstance();
// Grouped imports
import { External } from 'external'; // External deps
import { internalUtil } from './utils'; // Internal utils
import type { MyType } from './types'; // TypesWhen to Use References
| Reference File | When to Load |
|---|---|
| Setting up TypeScript configuration |
| Defining interfaces, generic types, extending external types |
| Working with discriminated unions, result types, exhaustive checks |
| Runtime validation, branded types, assertion functions |
| Using built-in utilities, const assertions, template literal types |
| Implementing structured errors, error factories, retry logic |
| Building generic services, repositories, factories, event emitters |
| Organizing imports/exports, avoiding circular dependencies |
Common Anti-Patterns to Avoid
- Using - Use
anywith type guards insteadunknown - Default exports - Harder to refactor and tree-shake
- Implicit return types on public APIs
- Non-readonly interfaces for immutable data
- Nested optionals () - use discriminated unions instead
{ a?: { b?: string } }