TypeScript Best Practices for Modern Web Development
TypeScript has become the de facto standard for large-scale JavaScript applications. Here are some essential best practices to help you write better TypeScript code.
1. Use Strict Mode
Always enable strict mode in your tsconfig.json
:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}
2. Prefer Type Inference
Let TypeScript infer types when possible:
// Good - type is inferred
const users = ['Alice', 'Bob', 'Charlie'];
// Unnecessary - type annotation is redundant
const users: string[] = ['Alice', 'Bob', 'Charlie'];
3. Use Union Types Effectively
Union types are powerful for modeling data that can be one of several types:
type Status = 'loading' | 'success' | 'error';
interface ApiResponse {
status: Status;
data?: any;
error?: string;
}
4. Leverage Utility Types
TypeScript provides many built-in utility types:
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
// Create a type with only some properties
type UserPreview = Pick<User, 'id' | 'name'>;
// Make all properties optional
type PartialUser = Partial<User>;
// Make all properties required
type RequiredUser = Required<User>;
5. Use Discriminated Unions
For complex state management, discriminated unions are invaluable:
type LoadingState = {
status: 'loading';
};
type SuccessState = {
status: 'success';
data: any;
};
type ErrorState = {
status: 'error';
error: string;
};
type AppState = LoadingState | SuccessState | ErrorState;
function handleState(state: AppState) {
switch (state.status) {
case 'loading':
// TypeScript knows this is LoadingState
break;
case 'success':
// TypeScript knows this is SuccessState
console.log(state.data);
break;
case 'error':
// TypeScript knows this is ErrorState
console.error(state.error);
break;
}
}
6. Generic Constraints
Use generic constraints to make your functions more flexible yet type-safe:
interface Identifiable {
id: number;
}
function updateEntity<T extends Identifiable>(
entity: T,
updates: Partial<T>
): T {
return { ...entity, ...updates };
}
Conclusion
These practices will help you write more maintainable and type-safe TypeScript code. Remember, the goal is to leverage TypeScript’s type system to catch errors at compile time and improve developer experience.