TypeScript Cheat Sheet
Strategic TypeScript guide for mid-level developers • Security-first approach • Performance patterns • Best practices
Sections
TypeScript Essentials & Security
Core concepts with security-first approach • Type safety • Runtime validation • Secure coding patterns
Type Safety & Runtime Validation
Strong typing for maintainability • Runtime validation prevents errors • Essential for production • Prevents injection attacks
// RECOMMENDED: Strong typing with validation - ALWAYS use in production
interface User {
readonly id: number;
name: string;
email: string;
role: "admin" | "user" | "guest";
createdAt: Date;
}
// RECOMMENDED: Type guards for runtime validation
function isValidUser(obj: unknown): obj is User {
return typeof obj === "object" &&
obj !== null &&
typeof (obj as User).id === "number" &&
typeof (obj as User).name === "string" &&
typeof (obj as User).email === "string" &&
["admin", "user", "guest"].includes((obj as User).role);
}
// SECURITY: SECURITY: Always validate external data
function processUserData(userData: unknown): User | null {
if (!isValidUser(userData)) {
console.error("Invalid user data received");
return null;
}
// Additional validation
if (!userData.email.includes("@") || userData.email.length < 5) {
console.error("Invalid email format");
return null;
}
return userData;
}
// RECOMMENDED: Branded types for additional safety
type UserId = number & { readonly brand: unique symbol };
type Email = string & { readonly brand: unique symbol };
function createUserId(id: number): UserId | null {
return id > 0 ? id as UserId : null;
}
function createEmail(email: string): Email | null {
return email.includes("@") && email.length >= 5 ? email as Email : null;
}
// TIP: WHEN TO USE: Type guards are essential for API data, user input, and any external sources
// TIP: WHY: Prevents runtime errors and security vulnerabilities
// TIP: COMMON MISTAKE: Trusting external data without validation
// TIP: PRODUCTION TIPS: Always log validation failures for monitoringInput Sanitization & Secure String Handling
Sanitize all user input • Prevent XSS attacks • Use template literals safely • Validate and escape content
// SECURITY: SECURITY: Input sanitization functions
function sanitizeHtmlInput(input: string): string {
return input
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
.replace(///g, "/");
}
function sanitizeUsername(username: string): string | null {
// Only allow alphanumeric and underscore
const cleaned = username.trim().toLowerCase();
const isValid = /^[a-zA-Z0-9_]{3,20}$/.test(cleaned);
return isValid ? cleaned : null;
}
// RECOMMENDED: Safe string operations with validation
class SafeString {
private constructor(private readonly value: string) {}
static create(input: string): SafeString | null {
if (typeof input !== "string" || input.length === 0) {
return null;
}
return new SafeString(sanitizeHtmlInput(input));
}
toString(): string {
return this.value;
}
concat(other: SafeString): SafeString {
return new SafeString(this.value + other.value);
}
}
// RECOMMENDED: Template literal type safety
type AllowedDomains = "example.com" | "test.com" | "myapp.com";
type EmailAddress<T extends AllowedDomains> = `${string}@${T}`;
function sendEmail<T extends AllowedDomains>(
to: EmailAddress<T>,
subject: string,
body: string
): boolean {
// Email sending logic with domain validation
const [, domain] = to.split("@");
const allowedDomains: AllowedDomains[] = ["example.com", "test.com", "myapp.com"];
if (!allowedDomains.includes(domain as AllowedDomains)) {
console.error(`Unauthorized domain: ${domain}`);
return false;
}
// Safe to send email
console.log(`Sending email to ${to}`);
return true;
}
// SECURITY: Path traversal prevention
function validateFilePath(userPath: string): string | null {
// Prevent directory traversal
if (userPath.includes("..") || userPath.includes("~") || userPath.startsWith("/")) {
console.warn(`Potential path traversal attempt: ${userPath}`);
return null;
}
// Only allow alphanumeric, dash, underscore, and dot
const sanitized = userPath.replace(/[^a-zA-Z0-9._-]/g, "");
return sanitized.length > 0 ? sanitized : null;
}
// TIP: SECURITY BEST PRACTICES:
// - Always sanitize user input before processing or storing
// - Use branded types for sensitive data that needs validation
// - Validate domains and file paths to prevent traversal attacks
// - Never trust external data without proper validation
// - Log all security violations for monitoring and incident responseNumbers & Type-Safe Math Operations
Handle numbers safely • Prevent overflow/underflow • Use appropriate numeric types • Validate arithmetic operations
// PERFORMANCE: Type-safe numeric operations
type SafeInteger = number & { readonly __brand: "SafeInteger" };
type PositiveNumber = number & { readonly __brand: "PositiveNumber" };
type Percentage = number & { readonly __brand: "Percentage" };
function createSafeInteger(value: number): SafeInteger | null {
if (!Number.isInteger(value) || !Number.isSafeInteger(value)) {
return null;
}
return value as SafeInteger;
}
function createPositiveNumber(value: number): PositiveNumber | null {
if (typeof value !== "number" || value <= 0 || !Number.isFinite(value)) {
return null;
}
return value as PositiveNumber;
}
function createPercentage(value: number): Percentage | null {
if (typeof value !== "number" || value < 0 || value > 100) {
return null;
}
return value as Percentage;
}
// RECOMMENDED: Safe arithmetic operations
class SafeMath {
static divide(a: number, b: number): number | null {
if (b === 0 || !Number.isFinite(a) || !Number.isFinite(b)) {
return null;
}
const result = a / b;
return Number.isFinite(result) ? result : null;
}
static add(a: number, b: number): number | null {
const result = a + b;
if (!Number.isFinite(result)) {
console.error("Addition resulted in non-finite number");
return null;
}
return result;
}
static multiply(a: number, b: number): number | null {
const result = a * b;
if (!Number.isFinite(result)) {
console.error("Multiplication resulted in non-finite number");
return null;
}
return result;
}
}
// SECURITY: Financial calculations with precision
class Money {
private readonly cents: SafeInteger;
constructor(dollars: number) {
const cents = Math.round(dollars * 100);
const safeCents = createSafeInteger(cents);
if (!safeCents) {
throw new Error(`Invalid monetary amount: ${dollars}`);
}
this.cents = safeCents;
}
add(other: Money): Money {
const newCents = createSafeInteger(this.cents + other.cents);
if (!newCents) {
throw new Error("Monetary addition overflow");
}
return new Money(newCents / 100);
}
multiply(factor: PositiveNumber): Money {
const result = SafeMath.multiply(this.cents, factor);
if (!result) {
throw new Error("Monetary multiplication error");
}
return new Money(result / 100);
}
toDollars(): number {
return this.cents / 100;
}
}
// RECOMMENDED: Range validation
function validateRange(value: number, min: number, max: number): boolean {
return typeof value === "number" &&
Number.isFinite(value) &&
value >= min &&
value <= max;
}
// TIP: WHEN TO USE:
// - Branded types: Financial calculations, IDs, measurements, sensitive data
// - SafeMath: Any user-input arithmetic operations to prevent overflow
// - Range validation: Age, scores, percentages, quantities, user inputs
// - Money class: All financial calculations to prevent precision errorsData Structures & Performance
Choose optimal structures • Understand performance implications • Memory-efficient patterns • Type-safe collections
Arrays vs Sets vs Maps - Performance Guide
Arrays: ordered access needed • Sets: fast membership testing • Maps: key-value performance • Choose based on use case
// PERFORMANCE: PERFORMANCE: Choose the right data structure
// RECOMMENDED: Arrays: Use for ordered data and frequent access by index
const users: readonly User[] = [
{ id: 1, name: "Alice", email: "alice@example.com", role: "admin", createdAt: new Date() },
{ id: 2, name: "Bob", email: "bob@example.com", role: "user", createdAt: new Date() }
];
// O(1) access by index
const firstUser = users[0];
// RECOMMENDED: Sets: Use for membership testing and unique values
const validRoles = new Set<User["role"]>(["admin", "user", "guest"]);
function isValidRole(role: string): role is User["role"] {
return validRoles.has(role as User["role"]); // O(1) lookup vs O(n) for arrays
}
// RECOMMENDED: Maps: Use for key-value associations with object keys
const userCache = new Map<number, User>();
function getUserById(id: number): User | null {
return userCache.get(id) || null; // O(1) lookup
}
function cacheUser(user: User): void {
userCache.set(user.id, user); // O(1) insertion
}
// PERFORMANCE: MEMORY: ReadonlyArray for immutable collections
type ImmutableUsers = readonly User[];
function processUsers(users: ImmutableUsers): ImmutableUsers {
// Functional approach - creates new array, doesn't mutate
return users.filter(user => user.role !== "guest")
.sort((a, b) => a.name.localeCompare(b.name));
}
// RECOMMENDED: Typed collections with constraints
class TypedSet<T> {
private readonly items = new Set<T>();
constructor(
private readonly validator: (item: T) => boolean,
private readonly maxSize: number = 1000
) {}
add(item: T): boolean {
if (!this.validator(item)) {
console.warn("Invalid item rejected");
return false;
}
if (this.items.size >= this.maxSize) {
console.warn("Set size limit reached");
return false;
}
this.items.add(item);
return true;
}
has(item: T): boolean {
return this.items.has(item);
}
size(): number {
return this.items.size;
}
toArray(): T[] {
return Array.from(this.items);
}
}
// Usage with validation
const emailSet = new TypedSet<string>(
(email) => email.includes("@") && email.length >= 5,
100
);
// TIP: PERFORMANCE COMPARISON:
// Array.includes(): O(n) - linear search, slow for large datasets
// Set.has(): O(1) - hash lookup, fast even for large datasets
// Map.get(): O(1) - hash lookup, efficient key-value operations
// Object property access: O(1) - but limited to string/symbol keys
// TIP: WHEN TO USE:
// - Array: Need indexing, ordered data, frequent iteration, small datasets
// - Set: Membership testing, removing duplicates, unique collections, fast lookups
// - Map: Key-value pairs with non-string keys, frequent lookups, complex keys
// - WeakMap/WeakSet: When you need garbage collection of keys for memory managementType-Safe Object Manipulation
Safe property access • Prevent runtime errors • Use proper typing • Handle optional properties
// SECURITY: Safe object operations with type guards
interface ApiResponse<T = unknown> {
data?: T;
error?: string;
status: number;
}
function isSuccessResponse<T>(response: ApiResponse): response is ApiResponse<T> & { data: T } {
return response.status >= 200 && response.status < 300 && response.data !== undefined;
}
function isErrorResponse(response: ApiResponse): response is ApiResponse & { error: string } {
return response.status >= 400 && response.error !== undefined;
}
// RECOMMENDED: Safe property access with default values
function getProperty<T, K extends keyof T>(obj: T, key: K, defaultValue: T[K]): T[K] {
const value = obj[key];
return value !== undefined ? value : defaultValue;
}
// RECOMMENDED: Safe object updates - immutable approach
function updateUser<K extends keyof User>(
user: User,
updates: Pick<User, K>
): User {
// Validate updates before applying
const validatedUpdates: Partial<User> = {};
for (const [key, value] of Object.entries(updates)) {
if (key === "email" && typeof value === "string") {
if (value.includes("@")) {
validatedUpdates.email = value;
}
} else if (key === "name" && typeof value === "string") {
if (value.trim().length > 0) {
validatedUpdates.name = value.trim();
}
} else if (key === "role" && typeof value === "string") {
if (validRoles.has(value as User["role"])) {
validatedUpdates.role = value as User["role"];
}
}
}
return { ...user, ...validatedUpdates };
}
// RECOMMENDED: Type-safe object transformation
function transformObject<T, U>(
obj: T,
transformer: (value: T[keyof T], key: keyof T) => U
): Record<keyof T, U> {
const result = {} as Record<keyof T, U>;
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = transformer(obj[key], key);
}
}
return result;
}
// RECOMMENDED: Deep readonly types for immutable objects
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly<T[P]>
: T[P];
};
// RECOMMENDED: Safe object merging
function mergeObjects<T extends Record<string, unknown>, U extends Record<string, unknown>>(
target: T,
source: U
): T & U {
// Validate that source doesn't override critical target properties
const protectedKeys = new Set(["id", "createdAt"]);
for (const key of Object.keys(source)) {
if (protectedKeys.has(key)) {
console.warn(`Attempted to override protected property: ${key}`);
delete (source as any)[key];
}
}
return { ...target, ...source };
}
// SECURITY: SECURITY: Prevent prototype pollution
function safeAssign<T extends Record<string, unknown>>(
target: T,
source: Record<string, unknown>
): T {
const result = { ...target };
for (const [key, value] of Object.entries(source)) {
// Skip dangerous keys
if (key === "__proto__" || key === "constructor" || key === "prototype") {
console.warn(`Dangerous key ignored: ${key}`);
continue;
}
(result as any)[key] = value;
}
return result;
}
// TIP: BEST PRACTICES:
// - Always validate object properties from external sources
// - Use readonly types for immutable data structures and shared state
// - Prefer immutable updates over mutations for predictable state changes
// - Protect against prototype pollution attacks in user-facing applications
// - Use type guards for runtime safety when dealing with unknown objectsControl Flow & Type Safety
Strategic type narrowing • Type guards • Error handling patterns • Async/await best practices
Type Guards & Defensive Programming
Validate at boundaries • Use discriminated unions • Early returns • Clear error paths
// RECOMMENDED: Strategic type guards - validate at system boundaries
type APIResponse<T> =
| { success: true; data: T }
| { success: false; error: string; code: number };
function isSuccessResponse<T>(response: APIResponse<T>): response is { success: true; data: T } {
return response.success === true;
}
function isErrorResponse<T>(response: APIResponse<T>): response is { success: false; error: string; code: number } {
return response.success === false;
}
// RECOMMENDED: Process API responses safely
async function fetchUserSafely(id: number): Promise<User | null> {
// Guard clause - validate input early
if (!Number.isInteger(id) || id <= 0) {
console.error(`Invalid user ID: ${id}`);
return null;
}
try {
const response: APIResponse<User> = await fetch(`/api/users/${id}`).then(r => r.json());
// Type narrowing with discriminated unions
if (isSuccessResponse(response)) {
// TypeScript knows response.data is User
if (!isValidUser(response.data)) {
console.error("Invalid user data from API");
return null;
}
return response.data;
}
if (isErrorResponse(response)) {
// TypeScript knows response.error and response.code exist
console.error(`API Error ${response.code}: ${response.error}`);
return null;
}
console.error("Unexpected API response format");
return null;
} catch (error) {
console.error("Network error:", error);
return null;
}
}
// RECOMMENDED: Exhaustive type checking with never
type Theme = "light" | "dark" | "auto";
function applyTheme(theme: Theme): void {
switch (theme) {
case "light":
document.body.className = "light-theme";
break;
case "dark":
document.body.className = "dark-theme";
break;
case "auto":
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
document.body.className = prefersDark ? "dark-theme" : "light-theme";
break;
default:
// This ensures all cases are handled
const exhaustive: never = theme;
throw new Error(`Unhandled theme: ${exhaustive}`);
}
}
// RECOMMENDED: Optional chaining and nullish coalescing for safety
interface NestedData {
user?: {
profile?: {
settings?: {
theme: Theme;
notifications: boolean;
};
};
};
}
function getThemePreference(data: NestedData): Theme {
// Safe navigation with fallback
return data.user?.profile?.settings?.theme ?? "auto";
}
function isNotificationsEnabled(data: NestedData): boolean {
return data.user?.profile?.settings?.notifications ?? true;
}
// TIP: DEFENSIVE PROGRAMMING:
// - Validate inputs at function boundaries to catch errors early
// - Use discriminated unions for complex state management
// - Handle all possible cases with never type for exhaustive checking
// - Use optional chaining for nested properties to prevent runtime errors
// - Always provide sensible defaults to ensure graceful degradationAsync Patterns & Error Handling
Type-safe promises • Handle rejections properly • Async iterators • Cancellation patterns
// RECOMMENDED: Type-safe async operations with proper error handling
type AsyncResult<T, E = Error> = Promise<
| { success: true; data: T }
| { success: false; error: E }
>;
async function safeAsyncOperation<T>(
operation: () => Promise<T>
): AsyncResult<T> {
try {
const data = await operation();
return { success: true, data };
} catch (error) {
const errorObj = error instanceof Error ? error : new Error(String(error));
return { success: false, error: errorObj };
}
}
// RECOMMENDED: Async operation with timeout
function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error(`Operation timed out after ${timeoutMs}ms`)), timeoutMs);
});
return Promise.race([promise, timeoutPromise]);
}
// RECOMMENDED: Parallel operations with error aggregation
async function fetchMultipleUsers(ids: number[]): Promise<{
users: User[];
errors: Array<{ id: number; error: string }>;
}> {
const results = await Promise.allSettled(
ids.map(async (id) => {
const result = await safeAsyncOperation(() => fetchUserSafely(id));
return { id, result };
})
);
const users: User[] = [];
const errors: Array<{ id: number; error: string }> = [];
results.forEach((result) => {
if (result.status === "fulfilled") {
const { id, result: userResult } = result.value;
if (userResult && userResult.success) {
users.push(userResult.data);
} else if (userResult && !userResult.success) {
errors.push({ id, error: userResult.error.message });
}
} else {
errors.push({ id: -1, error: result.reason });
}
});
return { users, errors };
}
// RECOMMENDED: Async iterators for streaming data
async function* fetchUsersStream(): AsyncIterableIterator<User> {
let page = 1;
const pageSize = 10;
while (true) {
const result = await safeAsyncOperation(() =>
fetch(`/api/users?page=${page}&size=${pageSize}`).then(r => r.json())
);
if (!result.success) {
console.error("Failed to fetch users:", result.error);
break;
}
const users = result.data;
if (users.length === 0) break;
for (const user of users) {
if (isValidUser(user)) {
yield user;
}
}
page++;
}
}
// RECOMMENDED: Cancellable async operations
class CancellableOperation<T> {
private controller = new AbortController();
private promise: Promise<T>;
constructor(
operation: (signal: AbortSignal) => Promise<T>
) {
this.promise = operation(this.controller.signal);
}
cancel(): void {
this.controller.abort();
}
async execute(): Promise<T> {
try {
return await this.promise;
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
throw new Error("Operation was cancelled");
}
throw error;
}
}
}
// Usage
const operation = new CancellableOperation(async (signal) => {
const response = await fetch("/api/data", { signal });
return response.json();
});
// Cancel if needed
setTimeout(() => operation.cancel(), 5000);
// TIP: ASYNC BEST PRACTICES:
// - Always handle both success and error cases explicitly
// - Use timeouts for external operations to prevent hanging
// - Aggregate errors in parallel operations for better debugging
// - Use async iterators for streaming data to manage memory efficiently
// - Implement cancellation for long-running operations to improve UXFunction Design & Error Handling
Pure functions • Proper error handling • Input validation • Functional programming patterns
Strategic Function Design
Pure functions preferred • Avoid side effects • Single responsibility • Clear error handling
// RECOMMENDED: Pure function - predictable, testable, no side effects
function calculateTax(amount: number, rate: number): number | null {
// Input validation
if (typeof amount !== "number" || typeof rate !== "number") {
return null;
}
if (amount < 0 || rate < 0 || rate > 1) {
return null;
}
if (!Number.isFinite(amount) || !Number.isFinite(rate)) {
return null;
}
return Math.round(amount * rate * 100) / 100; // Round to 2 decimal places
}
// RECOMMENDED: Function composition for complex operations
type Validator<T> = (value: T) => boolean;
type Transformer<T, U> = (value: T) => U;
function compose<T, U, V>(
validator: Validator<T>,
transformer: Transformer<T, U>,
finalValidator: Validator<U>
) {
return function(input: T): U | null {
if (!validator(input)) {
return null;
}
const transformed = transformer(input);
if (!finalValidator(transformed)) {
return null;
}
return transformed;
};
}
// RECOMMENDED: Higher-order function for retry logic
function withRetry<T extends unknown[], R>(
fn: (...args: T) => Promise<R>,
maxRetries: number = 3,
delayMs: number = 1000
) {
return async function(...args: T): Promise<R> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn(...args);
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
if (attempt === maxRetries) {
throw lastError;
}
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
}
}
throw lastError;
};
}
// RECOMMENDED: Function overloading for flexible APIs
function processData(input: string): string;
function processData(input: number): number;
function processData(input: User): User;
function processData(input: string | number | User): string | number | User {
if (typeof input === "string") {
return input.trim().toLowerCase();
}
if (typeof input === "number") {
return Math.max(0, input);
}
// User object - return validated copy
if (isValidUser(input)) {
return { ...input, name: input.name.trim() };
}
throw new Error("Invalid input type");
}
// RECOMMENDED: Currying for configuration
function createValidator<T>(
validationRules: Array<(value: T) => boolean>,
errorMessages: string[]
) {
return function(value: T): { isValid: boolean; errors: string[] } {
const errors: string[] = [];
validationRules.forEach((rule, index) => {
if (!rule(value)) {
errors.push(errorMessages[index]);
}
});
return {
isValid: errors.length === 0,
errors
};
};
}
// Usage
const userValidator = createValidator<User>(
[
(user) => user.name.length >= 2,
(user) => user.email.includes("@"),
(user) => ["admin", "user", "guest"].includes(user.role)
],
[
"Name must be at least 2 characters",
"Email must contain @",
"Role must be valid"
]
);
// TIP: WHEN TO USE:
// - Pure functions: Calculations, transformations, business logic that doesn't depend on external state
// - Function composition: Complex data processing pipelines that need to be maintainable
// - Higher-order functions: Cross-cutting concerns like retry, caching, logging, validation
// - Currying: Configuration, specialized validators, and creating reusable function factoriesException Handling & Result Types
Type-safe error handling • Result types • Never fail silently • Comprehensive logging
// RECOMMENDED: Result type for better error handling
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function createResult<T>(data: T): Result<T, never> {
return { success: true, data };
}
function createError<E>(error: E): Result<never, E> {
return { success: false, error };
}
// RECOMMENDED: Safe operation wrapper
async function safeOperation<T>(
operation: () => Promise<T>,
context?: string
): Promise<Result<T, string>> {
try {
const data = await operation();
return createResult(data);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
const contextMessage = context ? `${context}: ${message}` : message;
console.error("Operation failed:", {
context,
error: message,
timestamp: new Date().toISOString()
});
return createError(contextMessage);
}
}
// RECOMMENDED: Custom error classes for specific domains
class ValidationError extends Error {
constructor(
message: string,
public readonly field: string,
public readonly value: unknown
) {
super(message);
this.name = "ValidationError";
}
}
class NetworkError extends Error {
constructor(
message: string,
public readonly status: number,
public readonly url: string
) {
super(message);
this.name = "NetworkError";
}
}
class BusinessError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly details?: Record<string, unknown>
) {
super(message);
this.name = "BusinessError";
}
}
// RECOMMENDED: Error boundary function for async operations
async function withErrorBoundary<T>(
operation: () => Promise<T>,
errorHandler: (error: Error) => T | null = () => null
): Promise<T | null> {
try {
return await operation();
} catch (error) {
if (error instanceof ValidationError) {
console.error("Validation failed:", {
field: error.field,
value: error.value,
message: error.message
});
} else if (error instanceof NetworkError) {
console.error("Network error:", {
status: error.status,
url: error.url,
message: error.message
});
} else if (error instanceof BusinessError) {
console.error("Business logic error:", {
code: error.code,
details: error.details,
message: error.message
});
} else {
console.error("Unexpected error:", error);
}
return errorHandler(error instanceof Error ? error : new Error(String(error)));
}
}
// RECOMMENDED: Assertion functions for development
function assert(condition: boolean, message: string): asserts condition {
if (!condition) {
throw new Error(`Assertion failed: ${message}`);
}
}
function assertNever(value: never): never {
throw new Error(`Unexpected value: ${value}`);
}
function assertIsNumber(value: unknown): asserts value is number {
if (typeof value !== "number") {
throw new ValidationError(
`Expected number, got ${typeof value}`,
"value",
value
);
}
}
// RECOMMENDED: Safe resource management
class ResourceManager<T> {
constructor(
private resource: T,
private cleanup: (resource: T) => void | Promise<void>
) {}
async use<R>(operation: (resource: T) => Promise<R>): Promise<Result<R, string>> {
try {
const result = await operation(this.resource);
return createResult(result);
} catch (error) {
return createError(error instanceof Error ? error.message : String(error));
} finally {
try {
await this.cleanup(this.resource);
} catch (cleanupError) {
console.error("Cleanup failed:", cleanupError);
}
}
}
}
// TIP: ERROR HANDLING STRATEGY:
// - Use Result types for operations that can fail gracefully
// - Create domain-specific error classes for better debugging and handling
// - Never fail silently - always log errors with context for monitoring
// - Implement proper cleanup in finally blocks to prevent resource leaks
// - Use assertions for development-time checks to catch bugs earlyFile I/O & Data Security
Safe file operations • Browser APIs • Local storage security • Network security • Data validation
Secure File Operations & Browser APIs
Safe file reading • Validate file types • Size limits • Handle encoding • Prevent malicious uploads
// RECOMMENDED: Safe file reading with comprehensive validation
interface FileValidationConfig {
maxSizeBytes: number;
allowedTypes: string[];
allowedExtensions: string[];
scanForMalware?: boolean;
}
class SecureFileHandler {
private static readonly DEFAULT_CONFIG: FileValidationConfig = {
maxSizeBytes: 10 * 1024 * 1024, // 10MB
allowedTypes: ['image/jpeg', 'image/png', 'text/plain', 'application/pdf'],
allowedExtensions: ['.jpg', '.jpeg', '.png', '.txt', '.pdf'],
scanForMalware: true
};
static async validateAndReadFile(
file: File,
config: Partial<FileValidationConfig> = {}
): Promise<{ success: true; data: string | ArrayBuffer } | { success: false; error: string }> {
const fullConfig = { ...this.DEFAULT_CONFIG, ...config };
// SECURITY: SECURITY: Validate file size to prevent DoS
if (file.size > fullConfig.maxSizeBytes) {
return {
success: false,
error: `File too large: ${file.size} bytes (max: ${fullConfig.maxSizeBytes})`
};
}
// SECURITY: SECURITY: Validate file type
if (!fullConfig.allowedTypes.includes(file.type)) {
return {
success: false,
error: `Invalid file type: ${file.type}`
};
}
// SECURITY: SECURITY: Validate file extension
const extension = file.name.toLowerCase().substring(file.name.lastIndexOf('.'));
if (!fullConfig.allowedExtensions.includes(extension)) {
return {
success: false,
error: `Invalid file extension: ${extension}`
};
}
// SECURITY: SECURITY: Check for suspicious file names
if (this.hasSuspiciousName(file.name)) {
return {
success: false,
error: "Suspicious file name detected"
};
}
try {
const data = await this.readFileContent(file);
// Additional content validation for text files
if (file.type.startsWith('text/') && typeof data === 'string') {
if (this.hasUnsafeContent(data)) {
return {
success: false,
error: "File contains unsafe content"
};
}
}
return { success: true, data };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Failed to read file"
};
}
}
private static hasSuspiciousName(filename: string): boolean {
const suspiciousPatterns = [
/\.exe$/i, /\.bat$/i, /\.cmd$/i, /\.scr$/i,
/\.vbs$/i, /\.js$/i, /\.jar$/i, /\.php$/i,
/\.\./, // Double dots
/<script/i, // Script tags in filename
];
return suspiciousPatterns.some(pattern => pattern.test(filename));
}
private static hasUnsafeContent(content: string): boolean {
const unsafePatterns = [
/<script[^>]*>/i,
/javascript:/i,
/data:text\/html/i,
/vbscript:/i,
/on\w+\s*=/i, // Event handlers
];
return unsafePatterns.some(pattern => pattern.test(content));
}
private static async readFileContent(file: File): Promise<string | ArrayBuffer> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
if (event.target?.result) {
resolve(event.target.result);
} else {
reject(new Error("Failed to read file"));
}
};
reader.onerror = () => reject(new Error("File reading error"));
// Read as text for text files, as array buffer for binary
if (file.type.startsWith('text/')) {
reader.readAsText(file);
} else {
reader.readAsArrayBuffer(file);
}
});
}
}
// RECOMMENDED: Usage example
async function handleFileUpload(fileInput: HTMLInputElement): Promise<void> {
const files = fileInput.files;
if (!files || files.length === 0) return;
for (const file of Array.from(files)) {
const result = await SecureFileHandler.validateAndReadFile(file, {
maxSizeBytes: 5 * 1024 * 1024, // 5MB limit
allowedTypes: ['image/jpeg', 'image/png']
});
if (result.success) {
console.log("File processed successfully:", file.name);
// Process the file data safely
} else {
console.error("File validation failed:", result.error);
// Show error to user
}
}
}
// TIP: SECURITY BEST PRACTICES:
// - Always validate file size, type, and extension to prevent malicious uploads
// - Scan file content for suspicious patterns before processing
// - Use Content Security Policy headers to prevent XSS through uploaded files
// - Store uploaded files outside web root with randomized names
// - Implement virus scanning for production applicationsSecure Local Storage & Session Management
Safe data persistence • Encrypt sensitive data • Validate stored data • Handle storage quota
// SECURITY: Secure storage wrapper with encryption and validation
interface StorageItem<T> {
data: T;
timestamp: number;
expiry?: number;
encrypted?: boolean;
}
class SecureStorage {
private static readonly PREFIX = 'app_';
private static readonly ENCRYPTION_KEY = 'your-secret-key'; // Use proper key management!
// RECOMMENDED: Store data securely with expiration
static setItem<T>(
key: string,
value: T,
options: {
expiryHours?: number;
encrypt?: boolean;
validateOnRead?: (data: T) => boolean;
} = {}
): boolean {
try {
const item: StorageItem<T> = {
data: value,
timestamp: Date.now(),
encrypted: options.encrypt,
};
if (options.expiryHours) {
item.expiry = Date.now() + (options.expiryHours * 60 * 60 * 1000);
}
let serialized = JSON.stringify(item);
// SECURITY: SECURITY: Encrypt sensitive data
if (options.encrypt) {
serialized = this.encrypt(serialized);
}
const storageKey = this.PREFIX + key;
localStorage.setItem(storageKey, serialized);
return true;
} catch (error) {
console.error('Failed to store item:', error);
return false;
}
}
// RECOMMENDED: Retrieve data with validation and expiry check
static getItem<T>(
key: string,
validator?: (data: unknown) => data is T
): T | null {
try {
const storageKey = this.PREFIX + key;
let stored = localStorage.getItem(storageKey);
if (!stored) return null;
// Decrypt if needed (detect encryption by trying to parse)
try {
JSON.parse(stored);
} catch {
// Likely encrypted, try to decrypt
stored = this.decrypt(stored);
}
const item: StorageItem<T> = JSON.parse(stored);
// Check expiry
if (item.expiry && Date.now() > item.expiry) {
this.removeItem(key);
return null;
}
// Validate data if validator provided
if (validator && !validator(item.data)) {
console.warn(`Invalid data found for key: ${key}`);
this.removeItem(key);
return null;
}
return item.data;
} catch (error) {
console.error('Failed to retrieve item:', error);
return null;
}
}
// RECOMMENDED: Safe removal
static removeItem(key: string): void {
const storageKey = this.PREFIX + key;
localStorage.removeItem(storageKey);
}
// RECOMMENDED: Clear all app data
static clearAll(): void {
const keys = Object.keys(localStorage);
keys.forEach(key => {
if (key.startsWith(this.PREFIX)) {
localStorage.removeItem(key);
}
});
}
// RECOMMENDED: Check storage quota
static getStorageInfo(): { used: number; available: number; percentage: number } {
try {
const test = 'test';
const total = 1024 * 1024 * 5; // Approximate 5MB limit
let used = 0;
// Calculate used space
for (const key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
used += localStorage[key].length + key.length;
}
}
return {
used,
available: total - used,
percentage: (used / total) * 100
};
} catch {
return { used: 0, available: 0, percentage: 0 };
}
}
// SECURITY: Simple encryption (use crypto-js or Web Crypto API in production)
private static encrypt(text: string): string {
// This is a simple XOR cipher for demo - use proper encryption in production!
return btoa(text.split('').map((char, i) =>
String.fromCharCode(char.charCodeAt(0) ^ this.ENCRYPTION_KEY.charCodeAt(i % this.ENCRYPTION_KEY.length))
).join(''));
}
private static decrypt(encrypted: string): string {
const decoded = atob(encrypted);
return decoded.split('').map((char, i) =>
String.fromCharCode(char.charCodeAt(0) ^ this.ENCRYPTION_KEY.charCodeAt(i % this.ENCRYPTION_KEY.length))
).join('');
}
}
// RECOMMENDED: Type-safe user session management
interface UserSession {
userId: number;
username: string;
role: 'admin' | 'user' | 'guest';
loginTime: number;
lastActivity: number;
}
class SessionManager {
private static readonly SESSION_KEY = 'user_session';
private static readonly SESSION_TIMEOUT_HOURS = 8;
static createSession(user: Omit<UserSession, 'loginTime' | 'lastActivity'>): boolean {
const session: UserSession = {
...user,
loginTime: Date.now(),
lastActivity: Date.now()
};
return SecureStorage.setItem(this.SESSION_KEY, session, {
expiryHours: this.SESSION_TIMEOUT_HOURS,
encrypt: true
});
}
static getSession(): UserSession | null {
return SecureStorage.getItem(this.SESSION_KEY, this.isValidSession);
}
static updateActivity(): void {
const session = this.getSession();
if (session) {
session.lastActivity = Date.now();
SecureStorage.setItem(this.SESSION_KEY, session, {
expiryHours: this.SESSION_TIMEOUT_HOURS,
encrypt: true
});
}
}
static logout(): void {
SecureStorage.removeItem(this.SESSION_KEY);
// Clear other sensitive data
SecureStorage.clearAll();
}
private static isValidSession(data: unknown): data is UserSession {
return typeof data === 'object' &&
data !== null &&
typeof (data as UserSession).userId === 'number' &&
typeof (data as UserSession).username === 'string' &&
['admin', 'user', 'guest'].includes((data as UserSession).role);
}
}
// TIP: STORAGE SECURITY BEST PRACTICES:
// - Never store sensitive data (passwords, tokens) in localStorage without encryption
// - Always validate data when reading from storage to prevent injection attacks
// - Implement expiration for all stored data to limit exposure window
// - Monitor storage quota to prevent denial of service
// - Clear sensitive data on logout and use secure session managementNetwork Security & API Communication
Secure HTTP requests • Request validation • Error handling • Rate limiting • CSRF protection
// RECOMMENDED: Secure HTTP client with comprehensive protection
interface RequestConfig {
timeout?: number;
retries?: number;
validateResponse?: boolean;
includeCredentials?: boolean;
csrfToken?: string;
}
interface ApiError {
code: string;
message: string;
status: number;
timestamp: Date;
}
class SecureHttpClient {
private static readonly DEFAULT_TIMEOUT = 10000;
private static readonly BASE_URL = process.env.NEXT_PUBLIC_API_URL || '';
private static readonly REQUEST_COUNTER = new Map<string, number>();
// RECOMMENDED: Rate limiting per endpoint
private static checkRateLimit(endpoint: string): boolean {
const now = Date.now();
const key = `${endpoint}_${Math.floor(now / 60000)}`; // Per minute window
const count = this.REQUEST_COUNTER.get(key) || 0;
if (count >= 100) { // 100 requests per minute limit
console.warn(`Rate limit exceeded for endpoint: ${endpoint}`);
return false;
}
this.REQUEST_COUNTER.set(key, count + 1);
return true;
}
// RECOMMENDED: Secure request wrapper
static async request<T>(
endpoint: string,
options: RequestInit & RequestConfig = {}
): Promise<Result<T, ApiError>> {
// SECURITY: SECURITY: Rate limiting
if (!this.checkRateLimit(endpoint)) {
return createError({
code: 'RATE_LIMITED',
message: 'Too many requests',
status: 429,
timestamp: new Date()
});
}
// SECURITY: SECURITY: Validate URL to prevent SSRF
if (!this.isValidEndpoint(endpoint)) {
return createError({
code: 'INVALID_ENDPOINT',
message: 'Invalid endpoint URL',
status: 400,
timestamp: new Date()
});
}
const {
timeout = this.DEFAULT_TIMEOUT,
retries = 3,
validateResponse = true,
includeCredentials = false,
csrfToken,
...fetchOptions
} = options;
// RECOMMENDED: Build secure headers
const headers = new Headers({
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest', // CSRF protection
...((fetchOptions.headers as Record<string, string>) || {})
});
// SECURITY: SECURITY: Add CSRF token
if (csrfToken) {
headers.set('X-CSRF-Token', csrfToken);
}
// SECURITY: SECURITY: Add security headers
headers.set('X-Content-Type-Options', 'nosniff');
const requestOptions: RequestInit = {
...fetchOptions,
headers,
credentials: includeCredentials ? 'include' : 'same-origin',
};
// RECOMMENDED: Implement retry logic with exponential backoff
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(`${this.BASE_URL}${endpoint}`, {
...requestOptions,
signal: controller.signal
});
clearTimeout(timeoutId);
// RECOMMENDED: Validate response
if (!response.ok) {
const errorData = await this.parseErrorResponse(response);
return createError(errorData);
}
// SECURITY: SECURITY: Validate content type
const contentType = response.headers.get('content-type');
if (validateResponse && contentType && !contentType.includes('application/json')) {
return createError({
code: 'INVALID_CONTENT_TYPE',
message: `Expected JSON, got ${contentType}`,
status: response.status,
timestamp: new Date()
});
}
const data = await response.json();
// RECOMMENDED: Validate response structure
if (validateResponse && !this.isValidResponse(data)) {
return createError({
code: 'INVALID_RESPONSE',
message: 'Response validation failed',
status: 422,
timestamp: new Date()
});
}
return createResult(data);
} catch (error) {
if (attempt === retries) {
return createError({
code: 'NETWORK_ERROR',
message: error instanceof Error ? error.message : 'Network request failed',
status: 0,
timestamp: new Date()
});
}
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
}
}
return createError({
code: 'MAX_RETRIES_EXCEEDED',
message: 'Request failed after maximum retries',
status: 0,
timestamp: new Date()
});
}
// RECOMMENDED: Type-safe API methods
static async get<T>(endpoint: string, config?: RequestConfig): Promise<Result<T, ApiError>> {
return this.request<T>(endpoint, { method: 'GET', ...config });
}
static async post<T>(
endpoint: string,
data: unknown,
config?: RequestConfig
): Promise<Result<T, ApiError>> {
return this.request<T>(endpoint, {
method: 'POST',
body: JSON.stringify(data),
...config
});
}
static async put<T>(
endpoint: string,
data: unknown,
config?: RequestConfig
): Promise<Result<T, ApiError>> {
return this.request<T>(endpoint, {
method: 'PUT',
body: JSON.stringify(data),
...config
});
}
static async delete<T>(endpoint: string, config?: RequestConfig): Promise<Result<T, ApiError>> {
return this.request<T>(endpoint, { method: 'DELETE', ...config });
}
// SECURITY: Validate endpoint to prevent SSRF attacks
private static isValidEndpoint(endpoint: string): boolean {
// Only allow relative URLs or whitelisted domains
if (endpoint.startsWith('/')) return true;
const allowedDomains = [
'api.yourdomain.com',
'secure-api.yourdomain.com'
];
try {
const url = new URL(endpoint);
return allowedDomains.includes(url.hostname);
} catch {
return false;
}
}
private static async parseErrorResponse(response: Response): Promise<ApiError> {
try {
const errorData = await response.json();
return {
code: errorData.code || 'UNKNOWN_ERROR',
message: errorData.message || response.statusText,
status: response.status,
timestamp: new Date()
};
} catch {
return {
code: 'PARSE_ERROR',
message: response.statusText || 'Unknown error',
status: response.status,
timestamp: new Date()
};
}
}
private static isValidResponse(data: unknown): boolean {
// Basic validation - implement according to your API schema
return typeof data === 'object' && data !== null;
}
}
// RECOMMENDED: Usage example with proper error handling
async function fetchUserProfile(userId: number): Promise<UserSession | null> {
const result = await SecureHttpClient.get<UserSession>(`/api/users/${userId}`, {
timeout: 5000,
validateResponse: true,
includeCredentials: true
});
if (result.success) {
return result.data;
} else {
console.error('Failed to fetch user profile:', {
code: result.error.code,
message: result.error.message,
status: result.error.status
});
return null;
}
}
// TIP: NETWORK SECURITY BEST PRACTICES:
// - Implement rate limiting to prevent abuse and DoS attacks
// - Validate all endpoints to prevent SSRF (Server-Side Request Forgery)
// - Use CSRF tokens for state-changing operations
// - Set secure headers to prevent common attacks (XSS, clickjacking)
// - Always validate response content and structure before processing
// - Implement proper error handling without exposing sensitive informationPerformance & Memory Optimization
Optimize bundle size • Lazy loading • Memory management • Efficient algorithms • Type-only imports
Bundle Optimization & Code Splitting
Reduce bundle size • Tree shaking • Dynamic imports • Type-only imports • Lazy loading patterns
// PERFORMANCE: Type-only imports for better tree shaking
import type { User, ApiResponse } from './types/user';
import type { ComponentProps } from 'react';
// Only import the actual implementation when needed
const loadUserModule = () => import('./modules/user');
const loadChartComponent = () => import('./components/Chart');
// RECOMMENDED: Lazy-loaded type-safe modules
interface LazyModule<T> {
load(): Promise<T>;
isLoaded(): boolean;
get(): T | null;
}
class LazyModuleLoader<T> implements LazyModule<T> {
private module: T | null = null;
private loading: Promise<T> | null = null;
constructor(private loader: () => Promise<{ default: T } | T>) {}
async load(): Promise<T> {
if (this.module) {
return this.module;
}
if (!this.loading) {
this.loading = this.loader().then(mod => {
// Handle both default exports and direct exports
const loadedModule = 'default' in mod ? mod.default : mod;
this.module = loadedModule;
this.loading = null;
return loadedModule;
});
}
return this.loading;
}
isLoaded(): boolean {
return this.module !== null;
}
get(): T | null {
return this.module;
}
}
// RECOMMENDED: Smart component lazy loading
interface LazyComponentProps<T> {
loader: () => Promise<{ default: React.ComponentType<T> }>;
fallback?: React.ComponentNode;
props: T;
}
function LazyComponent<T>({ loader, fallback, props }: LazyComponentProps<T>) {
const [Component, setComponent] = React.useState<React.ComponentType<T> | null>(null);
const [error, setError] = React.useState<string | null>(null);
React.useEffect(() => {
loader()
.then(({ default: LoadedComponent }) => {
setComponent(() => LoadedComponent);
})
.catch((err) => {
setError(err instanceof Error ? err.message : 'Failed to load component');
});
}, [loader]);
if (error) {
return <div>Error loading component: {error}</div>;
}
if (!Component) {
return fallback || <div>Loading...</div>;
}
return <Component {...props} />;
}
// RECOMMENDED: Bundle analyzer utility
interface BundleAnalysis {
totalSize: number;
gzippedSize: number;
modules: Array<{ name: string; size: number; }>;
duplicates: string[];
}
class BundleOptimizer {
// RECOMMENDED: Detect unused exports (for build-time analysis)
static analyzeImports(sourceCode: string): {
imports: string[];
unusedImports: string[];
typeOnlyImports: string[];
} {
const importRegex = /import\s+(?:{([^}]+)}|([\w*]+))\s+from\s+['"]([^'"]+)['"]/g;
const typeImportRegex = /import\s+type\s+{([^}]+)}\s+from\s+['"]([^'"]+)['"]/g;
const imports: string[] = [];
const typeOnlyImports: string[] = [];
let match;
// Find type-only imports
while ((match = typeImportRegex.exec(sourceCode)) !== null) {
typeOnlyImports.push(...match[1].split(',').map(s => s.trim()));
}
// Find regular imports
while ((match = importRegex.exec(sourceCode)) !== null) {
if (match[1]) {
imports.push(...match[1].split(',').map(s => s.trim()));
} else if (match[2]) {
imports.push(match[2]);
}
}
const unusedImports = imports.filter(imp => {
// Simple check if import name appears elsewhere in code
const count = sourceCode.split(imp).length - 1;
return count <= 1; // Only appears once (in import)
});
return { imports, unusedImports, typeOnlyImports };
}
// RECOMMENDED: Dynamic import with preloading
static createPreloadableImport<T>(
loader: () => Promise<T>,
preloadCondition?: () => boolean
) {
let preloaded: Promise<T> | null = null;
return {
preload(): Promise<T> {
if (!preloaded) {
preloaded = loader();
}
return preloaded;
},
load(): Promise<T> {
if (preloaded) {
return preloaded;
}
return loader();
},
autoPreload(): void {
if (preloadCondition && preloadCondition()) {
this.preload();
}
}
};
}
}
// RECOMMENDED: Webpack bundle splitting optimization hints
/* webpack/tsconfig.json configuration:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"allowImportingTsExtensions": false,
"noEmit": true,
"isolatedModules": true,
"verbatimModuleSyntax": true // Preserves type-only imports
}
}
// webpack.config.js optimization:
{
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
enforce: true
}
}
},
usedExports: true, // Enable tree shaking
sideEffects: false, // Mark package as side-effect free
}
}
*/
// TIP: BUNDLE OPTIMIZATION STRATEGIES:
// - Use "type" imports exclusively for types to enable better tree shaking
// - Implement dynamic imports for large components and libraries
// - Preload critical modules during idle time to improve perceived performance
// - Analyze bundle composition regularly to identify optimization opportunities
// - Configure webpack/bundler properly for optimal code splitting and tree shakingMemory Management & Efficient Data Structures
Prevent memory leaks • Optimize collections • Weak references • Object pooling • Garbage collection awareness
// RECOMMENDED: Memory-efficient data structures
class LRUCache<K, V> {
private cache = new Map<K, V>();
private maxSize: number;
constructor(maxSize: number = 100) {
this.maxSize = maxSize;
}
get(key: K): V | undefined {
if (this.cache.has(key)) {
// Move to end (most recently used)
const value = this.cache.get(key)!;
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
return undefined;
}
set(key: K, value: V): void {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.maxSize) {
// Remove least recently used (first item)
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
clear(): void {
this.cache.clear();
}
size(): number {
return this.cache.size;
}
// PERFORMANCE: Memory usage estimation
getMemoryUsage(): number {
let size = 0;
for (const [key, value] of this.cache) {
size += this.estimateSize(key) + this.estimateSize(value);
}
return size;
}
private estimateSize(obj: unknown): number {
if (typeof obj === 'string') return obj.length * 2; // UTF-16
if (typeof obj === 'number') return 8;
if (typeof obj === 'boolean') return 1;
if (obj === null || obj === undefined) return 0;
if (typeof obj === 'object') {
return JSON.stringify(obj).length * 2; // Rough estimate
}
return 0;
}
}
// RECOMMENDED: Weak references for memory management
class WeakCache<T extends object, V> {
private cache = new WeakMap<T, V>();
get(key: T): V | undefined {
return this.cache.get(key);
}
set(key: T, value: V): void {
this.cache.set(key, value);
}
has(key: T): boolean {
return this.cache.has(key);
}
delete(key: T): boolean {
return this.cache.delete(key);
}
}
// RECOMMENDED: Object pooling for frequent allocations
class ObjectPool<T> {
private pool: T[] = [];
private createFn: () => T;
private resetFn: (obj: T) => void;
private maxSize: number;
constructor(
createFn: () => T,
resetFn: (obj: T) => void,
initialSize: number = 10,
maxSize: number = 100
) {
this.createFn = createFn;
this.resetFn = resetFn;
this.maxSize = maxSize;
// Pre-populate pool
for (let i = 0; i < initialSize; i++) {
this.pool.push(createFn());
}
}
acquire(): T {
const obj = this.pool.pop();
return obj || this.createFn();
}
release(obj: T): void {
if (this.pool.length < this.maxSize) {
this.resetFn(obj);
this.pool.push(obj);
}
// If pool is full, let object be garbage collected
}
size(): number {
return this.pool.length;
}
clear(): void {
this.pool.length = 0;
}
}
// RECOMMENDED: Memory leak detection and prevention
class MemoryLeakDetector {
private static readonly observers = new Set<() => void>();
private static readonly timers = new Set<number>();
private static readonly intervals = new Set<number>();
// Track event listeners for cleanup
static addEventListener<K extends keyof DocumentEventMap>(
element: Document | Element,
type: K,
listener: (event: DocumentEventMap[K]) => void,
options?: boolean | AddEventListenerOptions
): () => void {
element.addEventListener(type, listener, options);
const cleanup = () => {
element.removeEventListener(type, listener, options);
};
this.observers.add(cleanup);
return cleanup;
}
// Track timers for cleanup
static setTimeout(callback: () => void, delay: number): number {
const id = window.setTimeout(() => {
this.timers.delete(id);
callback();
}, delay);
this.timers.add(id);
return id;
}
static setInterval(callback: () => void, delay: number): number {
const id = window.setInterval(callback, delay);
this.intervals.add(id);
return id;
}
static clearTimeout(id: number): void {
window.clearTimeout(id);
this.timers.delete(id);
}
static clearInterval(id: number): void {
window.clearInterval(id);
this.intervals.delete(id);
}
// Clean up all tracked resources
static cleanup(): void {
// Clear all observers
this.observers.forEach(cleanup => cleanup());
this.observers.clear();
// Clear all timers
this.timers.forEach(id => window.clearTimeout(id));
this.timers.clear();
// Clear all intervals
this.intervals.forEach(id => window.clearInterval(id));
this.intervals.clear();
}
// Memory usage monitoring
static getMemoryInfo(): {
usedJSHeapSize: number;
totalJSHeapSize: number;
jsHeapSizeLimit: number;
} | null {
if ('memory' in performance) {
const memory = (performance as any).memory;
return {
usedJSHeapSize: memory.usedJSHeapSize,
totalJSHeapSize: memory.totalJSHeapSize,
jsHeapSizeLimit: memory.jsHeapSizeLimit
};
}
return null;
}
}
// RECOMMENDED: Optimized collection operations
class OptimizedArray<T> {
private items: T[] = [];
private indices = new Map<T, number>();
add(item: T): void {
if (!this.indices.has(item)) {
const index = this.items.length;
this.items.push(item);
this.indices.set(item, index);
}
}
remove(item: T): boolean {
const index = this.indices.get(item);
if (index === undefined) return false;
// Swap with last element and pop (O(1) removal)
const lastItem = this.items[this.items.length - 1];
this.items[index] = lastItem;
this.indices.set(lastItem, index);
this.items.pop();
this.indices.delete(item);
return true;
}
has(item: T): boolean {
return this.indices.has(item);
}
toArray(): readonly T[] {
return this.items;
}
clear(): void {
this.items.length = 0;
this.indices.clear();
}
}
// RECOMMENDED: React component memory optimization
interface ComponentCleanup {
cleanup(): void;
}
function useMemoryOptimization(): ComponentCleanup {
const cleanupFunctions = React.useRef<(() => void)[]>([]);
const addCleanup = React.useCallback((fn: () => void) => {
cleanupFunctions.current.push(fn);
}, []);
const cleanup = React.useCallback(() => {
cleanupFunctions.current.forEach(fn => fn());
cleanupFunctions.current = [];
}, []);
React.useEffect(() => {
return cleanup; // Cleanup on unmount
}, [cleanup]);
return { cleanup };
}
// Usage examples
const stringPool = new ObjectPool(
() => '', // Create empty string
(str) => { /* Reset string if needed */ }, // Reset function
20, // Initial pool size
100 // Max pool size
);
const userCache = new LRUCache<number, User>(50);
const componentCache = new WeakCache<HTMLElement, ComponentData>();
// TIP: MEMORY OPTIMIZATION STRATEGIES:
// - Use LRU cache for frequently accessed data with size limits
// - Implement object pooling for frequently created/destroyed objects
// - Use WeakMap/WeakSet for object associations that shouldn't prevent garbage collection
// - Track and clean up event listeners, timers, and other resources in React components
// - Monitor memory usage in development to identify and fix memory leaks earlyAdvanced TypeScript Patterns
Modern TypeScript features • Utility types • Template literals • Performance optimization
Advanced Utility Types & Transformations
Custom utility types • Type transformations • Template literal magic • Conditional types
// RECOMMENDED: Advanced utility types for API design
type StrictOmit<T, K extends keyof T> = Omit<T, K>;
type StrictPick<T, K extends keyof T> = Pick<T, K>;
// RECOMMENDED: Deep partial for nested objects
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object
? T[P] extends Array<infer U>
? Array<DeepPartial<U>>
: DeepPartial<T[P]>
: T[P];
};
// RECOMMENDED: Type-safe path access
type PathsToStringProps<T> = T extends string
? []
: {
[K in Extract<keyof T, string>]: T[K] extends string
? [K]
: T[K] extends object
? [K, ...PathsToStringProps<T[K]>]
: never;
}[Extract<keyof T, string>];
type Join<T extends string[], D extends string> = T extends readonly [
infer F,
...infer R
]
? F extends string
? R extends readonly string[]
? R['length'] extends 0
? F
: `${F}${D}${Join<R, D>}`
: never
: never
: '';
// Usage for nested property access
type UserPaths = Join<PathsToStringProps<User>, '.'>;
// Result: "name" | "email" | "role"
// RECOMMENDED: Event system with template literals
type EventMap = {
'user:created': { user: User };
'user:updated': { user: User; changes: Partial<User> };
'user:deleted': { userId: number };
'system:error': { error: Error; context: string };
};
type EventName = keyof EventMap;
type EventPayload<T extends EventName> = EventMap[T];
class TypedEventEmitter {
private listeners = new Map<EventName, Function[]>();
on<T extends EventName>(
event: T,
listener: (payload: EventPayload<T>) => void
): void {
const existing = this.listeners.get(event) || [];
this.listeners.set(event, [...existing, listener]);
}
emit<T extends EventName>(event: T, payload: EventPayload<T>): void {
const listeners = this.listeners.get(event) || [];
listeners.forEach(listener => listener(payload));
}
off<T extends EventName>(
event: T,
listener: (payload: EventPayload<T>) => void
): void {
const existing = this.listeners.get(event) || [];
this.listeners.set(event, existing.filter(l => l !== listener));
}
}
// RECOMMENDED: Builder pattern with method chaining
class QueryBuilder<T> {
private filters: Array<(item: T) => boolean> = [];
private sortFn?: (a: T, b: T) => number;
private limitCount?: number;
where(predicate: (item: T) => boolean): this {
this.filters.push(predicate);
return this;
}
sortBy<K extends keyof T>(key: K, direction: 'asc' | 'desc' = 'asc'): this {
this.sortFn = (a, b) => {
const aVal = a[key];
const bVal = b[key];
const modifier = direction === 'asc' ? 1 : -1;
if (aVal < bVal) return -1 * modifier;
if (aVal > bVal) return 1 * modifier;
return 0;
};
return this;
}
limit(count: number): this {
this.limitCount = count;
return this;
}
execute(data: T[]): T[] {
let result = data.filter(item =>
this.filters.every(filter => filter(item))
);
if (this.sortFn) {
result = result.sort(this.sortFn);
}
if (this.limitCount) {
result = result.slice(0, this.limitCount);
}
return result;
}
}
// Usage
const userQuery = new QueryBuilder<User>()
.where(user => user.role === "admin")
.sortBy("name", "asc")
.limit(10);
// TIP: ADVANCED PATTERNS:
// - Use conditional types for flexible APIs that adapt to input types
// - Template literals for type-safe string operations and domain-specific languages
// - Builder pattern for complex object construction with method chaining
// - Event systems with strict payload typing for type-safe communicationTesting & Debugging Best Practices
Type-safe testing • Mock external dependencies • Debug effectively • Comprehensive coverage
Strategic Testing Patterns
Test type safety • Mock dependencies • Test error conditions • Use proper assertions
// RECOMMENDED: Type-safe test utilities
interface TestUser extends User {
readonly _testId: unique symbol;
}
function createTestUser(overrides: Partial<User> = {}): TestUser {
return {
id: Math.floor(Math.random() * 1000),
name: "Test User",
email: "test@example.com",
role: "user" as const,
createdAt: new Date(),
...overrides,
_testId: Symbol()
} as TestUser;
}
// RECOMMENDED: Type-safe mocking
interface MockApi {
getUser: jest.MockedFunction<(id: number) => Promise<User>>;
updateUser: jest.MockedFunction<(id: number, data: Partial<User>) => Promise<User>>;
deleteUser: jest.MockedFunction<(id: number) => Promise<void>>;
}
function createMockApi(): MockApi {
return {
getUser: jest.fn(),
updateUser: jest.fn(),
deleteUser: jest.fn()
};
}
// RECOMMENDED: Test fixtures with type safety
class TestFixtures {
static users = {
admin: (): TestUser => createTestUser({
role: "admin",
name: "Admin User",
email: "admin@example.com"
}),
regular: (): TestUser => createTestUser({
role: "user",
name: "Regular User",
email: "user@example.com"
}),
guest: (): TestUser => createTestUser({
role: "guest",
name: "Guest User",
email: "guest@example.com"
})
};
static apiResponses = {
success: <T>(data: T): APIResponse<T> => ({
success: true,
data
}),
error: (message: string, code: number = 500): APIResponse<never> => ({
success: false,
error: message,
code
})
};
}
// RECOMMENDED: Comprehensive test suite
describe("UserService", () => {
let mockApi: MockApi;
let userService: UserService;
beforeEach(() => {
mockApi = createMockApi();
userService = new UserService(mockApi);
});
describe("getUser", () => {
it("should return user for valid ID", async () => {
// Arrange
const testUser = TestFixtures.users.regular();
const response = TestFixtures.apiResponses.success(testUser);
mockApi.getUser.mockResolvedValue(response.data);
// Act
const result = await userService.getUser(testUser.id);
// Assert
expect(result).toEqual(testUser);
expect(mockApi.getUser).toHaveBeenCalledWith(testUser.id);
expect(mockApi.getUser).toHaveBeenCalledTimes(1);
});
it("should handle API errors gracefully", async () => {
// Arrange
const errorMessage = "User not found";
mockApi.getUser.mockRejectedValue(new Error(errorMessage));
// Act & Assert
await expect(userService.getUser(999)).rejects.toThrow(errorMessage);
expect(mockApi.getUser).toHaveBeenCalledWith(999);
});
it("should validate user ID input", async () => {
// Test edge cases
await expect(userService.getUser(-1)).rejects.toThrow("Invalid user ID");
await expect(userService.getUser(0)).rejects.toThrow("Invalid user ID");
await expect(userService.getUser(NaN)).rejects.toThrow("Invalid user ID");
});
});
describe("updateUser", () => {
it("should update user with valid data", async () => {
// Arrange
const originalUser = TestFixtures.users.regular();
const updates = { name: "Updated Name" };
const updatedUser = { ...originalUser, ...updates };
mockApi.updateUser.mockResolvedValue(updatedUser);
// Act
const result = await userService.updateUser(originalUser.id, updates);
// Assert
expect(result.name).toBe("Updated Name");
expect(mockApi.updateUser).toHaveBeenCalledWith(originalUser.id, updates);
});
it("should reject invalid email updates", async () => {
// Test validation
const user = TestFixtures.users.regular();
const invalidUpdate = { email: "invalid-email" };
await expect(
userService.updateUser(user.id, invalidUpdate)
).rejects.toThrow("Invalid email format");
});
});
});
// RECOMMENDED: Property-based testing
import { fc } from 'fast-check';
describe("User validation", () => {
it("should always validate email format", () => {
fc.assert(
fc.property(
fc.string().filter(s => s.includes("@") && s.length >= 5),
(email) => {
const user = createTestUser({ email });
expect(isValidUser(user)).toBe(true);
}
)
);
});
it("should reject invalid emails", () => {
fc.assert(
fc.property(
fc.string().filter(s => !s.includes("@") || s.length < 5),
(email) => {
const user = createTestUser({ email });
expect(isValidUser(user)).toBe(false);
}
)
);
});
});
// TIP: TESTING BEST PRACTICES:
// - Create type-safe test utilities and fixtures for consistent testing
// - Mock external dependencies with proper typing to maintain type safety
// - Test both success and error conditions to ensure robustness
// - Use property-based testing for validation logic to catch edge cases
// - Test edge cases and boundary conditions that might be missed in manual testingDebugging & Error Investigation
Type-safe logging • Debug utilities • Performance profiling • Error tracking
// RECOMMENDED: Type-safe logging system
enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3
}
interface LogEntry {
level: LogLevel;
message: string;
timestamp: Date;
context?: Record<string, unknown>;
error?: Error;
}
class TypedLogger {
private minLevel: LogLevel = LogLevel.INFO;
setLevel(level: LogLevel): void {
this.minLevel = level;
}
private log(level: LogLevel, message: string, context?: Record<string, unknown>, error?: Error): void {
if (level < this.minLevel) return;
const entry: LogEntry = {
level,
message,
timestamp: new Date(),
context,
error
};
console.log(JSON.stringify(entry, null, 2));
}
debug(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.DEBUG, message, context);
}
info(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.INFO, message, context);
}
warn(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.WARN, message, context);
}
error(message: string, error?: Error, context?: Record<string, unknown>): void {
this.log(LogLevel.ERROR, message, context, error);
}
}
const logger = new TypedLogger();
// RECOMMENDED: Debug utilities with type information
function debugValue<T>(label: string, value: T): T {
if (process.env.NODE_ENV === 'development') {
logger.debug(`Debug: ${label}`, {
type: typeof value,
value: value,
constructor: value?.constructor?.name
});
}
return value;
}
function debugFunction<T extends (...args: any[]) => any>(fn: T): T {
return ((...args: Parameters<T>): ReturnType<T> => {
const start = performance.now();
logger.debug(`Calling ${fn.name}`, { args });
try {
const result = fn(...args);
const duration = performance.now() - start;
logger.debug(`${fn.name} completed`, {
duration: `${duration.toFixed(2)}ms`,
result: typeof result === 'object' ? '[Object]' : result
});
return result;
} catch (error) {
const duration = performance.now() - start;
logger.error(`${fn.name} failed`, error as Error, {
duration: `${duration.toFixed(2)}ms`,
args
});
throw error;
}
}) as T;
}
// RECOMMENDED: Performance profiling
class PerformanceProfiler {
private static marks = new Map<string, number>();
static mark(label: string): void {
this.marks.set(label, performance.now());
}
static measure(label: string, startMark: string): number {
const startTime = this.marks.get(startMark);
if (!startTime) {
logger.warn(`Start mark not found: ${startMark}`);
return 0;
}
const duration = performance.now() - startTime;
logger.info(`Performance: ${label}`, {
duration: `${duration.toFixed(2)}ms`,
startMark
});
return duration;
}
static profile<T>(label: string, fn: () => T): T {
const start = performance.now();
try {
const result = fn();
const duration = performance.now() - start;
logger.info(`Profile: ${label}`, { duration: `${duration.toFixed(2)}ms` });
return result;
} catch (error) {
const duration = performance.now() - start;
logger.error(`Profile failed: ${label}`, error as Error, {
duration: `${duration.toFixed(2)}ms`
});
throw error;
}
}
}
// RECOMMENDED: Error boundary with context
class ErrorTracker {
private static errors: Array<{
error: Error;
context: Record<string, unknown>;
timestamp: Date;
userAgent?: string;
url?: string;
}> = [];
static captureError(
error: Error,
context: Record<string, unknown> = {}
): void {
const errorInfo = {
error,
context,
timestamp: new Date(),
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
url: typeof window !== 'undefined' ? window.location.href : undefined
};
this.errors.push(errorInfo);
logger.error('Error captured', error, {
context,
userAgent: errorInfo.userAgent,
url: errorInfo.url
});
// Send to error tracking service in production
if (process.env.NODE_ENV === 'production') {
this.sendToErrorService(errorInfo);
}
}
private static async sendToErrorService(errorInfo: any): Promise<void> {
try {
// Implementation for error tracking service
await fetch('/api/errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(errorInfo)
});
} catch (e) {
logger.warn('Failed to send error to tracking service', { error: e });
}
}
static getErrors(): readonly typeof ErrorTracker.errors[number][] {
return this.errors;
}
}
// RECOMMENDED: Type-safe debugging hooks
function useDebugValue<T>(value: T, label: string): T {
if (process.env.NODE_ENV === 'development') {
React.useDebugValue(`${label}: ${JSON.stringify(value)}`);
}
return value;
}
// Usage examples
const fetchUserWithDebug = debugFunction(fetchUserSafely);
const profiledOperation = () => PerformanceProfiler.profile('userData', () => {
return processUserData(userData);
});
// TIP: DEBUGGING STRATEGIES:
// - Use structured logging with context for better error tracking
// - Profile performance-critical functions to identify bottlenecks
// - Capture errors with full context information for effective debugging
// - Use type-safe debug utilities in development to maintain type safety
// - Monitor and track errors in production for continuous improvement