Best Practices
This comprehensive guide outlines industry-standard best practices for TypeScript development within the ts-playground project, designed to align with professional backend development standards.
TypeScript Advanced Best Practices
Advanced Type Manipulation
Conditional Types for Dynamic Behavior:
type IsString<T> = T extends string ? true : false;
type StringStatus = IsString<"hello">; // true
// Advanced utility for API response handling
type ApiResponse<T> = {
success: true;
data: T;
} | {
success: false;
error: string;
};
Generic Constraints with Multiple Bounds:
interface Lengthwise {
length: number;
}
function getProperty<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
Discriminated Unions for Type Safety
interface LoadingState {
status: 'loading';
progress?: number;
}
interface SuccessState<T> {
status: 'success';
data: T;
timestamp: Date;
}
interface ErrorState {
status: 'error';
error: Error;
}
type ApiState<T> = LoadingState | SuccessState<T> | ErrorState;
function handleApiState<T>(state: ApiState<T>) {
switch (state.status) {
case 'loading':
console.log(`Loading: ${state.progress || 0}%`);
break;
case 'success':
console.log(`Success at ${state.timestamp}`);
break;
case 'error':
console.error(state.error.message);
break;
}
}
Const Assertions for Immutable Data
const API_CONFIG = {
endpoints: {
users: '/api/users',
posts: '/api/posts',
},
timeout: 5000,
} as const;
// Results in:
// - API_CONFIG.endpoints.users being of type '/api/users' (not string)
// - All properties becoming readonly
Architectural Best Practices
Layered Architecture Pattern
// Domain Layer: Pure business logic
class UserDomain {
constructor(private readonly validationRules: ValidationRules) {}
async validate(user: Partial<User>): Promise<ValidationResult> {
return this.validationRules.validate(user);
}
}
// Application Layer: Orchestrates domain and infrastructure
class UserService {
constructor(
private readonly userDomain: UserDomain,
private readonly userRepository: UserRepository,
private readonly logger: Logger
) {}
async createUser(userData: UserCreationRequest): Promise<User> {
try {
const validation = await this.userDomain.validate(userData);
if (!validation.isValid) throw new ValidationError(validation.errors);
const user = await this.userRepository.create(userData);
this.logger.info(`User created: ${user.id}`);
return user;
} catch (error) {
this.logger.error('User creation failed', error);
throw error;
}
}
}
// Infrastructure Layer: External integrations
interface UserRepository {
create(data: UserCreationRequest): Promise<User>;
findById(id: string): Promise<User | null>;
}