TypeScript Performance Optimization
/ 6 min read
TypeScript Performance Optimization
Table of Contents
- Type System Optimization
- Memory Management
- Runtime Performance
- Build Time Optimization
- Framework-Specific Optimizations
Type System Optimization
Type Inference Optimization
// ❌ Poor type inferenceconst items = []; // Type: any[]const data = {}; // Type: any
// ✅ Better type inferenceconst items: string[] = [];const data: Record<string, unknown> = {};
// ✅ Best: Let TypeScript infer from initializationconst items = ['a', 'b', 'c']; // Type: string[]const data = { name: 'John', age: 30}; // Type: { name: string; age: number; }Union Type Optimization
// ❌ Expensive union typetype ExpensiveUnion = | { type: 'a'; data: string } | { type: 'b'; data: number } | { type: 'c'; data: boolean } | { type: 'd'; data: object } // ... many more variants
// ✅ Optimized discriminated uniontype ActionType = 'a' | 'b' | 'c' | 'd';
interface Action { type: ActionType; data: unknown;}
// ✅ Type narrowing for better performancefunction processAction(action: Action) { switch (action.type) { case 'a': return processStringData(action.data as string); case 'b': return processNumberData(action.data as number); // ... }}Generic Constraints
// ❌ Unconstrained genericsfunction processData<T>(data: T) { // TypeScript has to consider all possible types return data;}
// ✅ Constrained genericsinterface DataItem { id: string; value: unknown;}
function processData<T extends DataItem>(data: T) { // TypeScript only needs to consider types that match DataItem return data.id;}
// ✅ Multiple constraints for better type inferencefunction merge< T extends object, U extends Partial<T>>(target: T, source: U): T & U { return { ...target, ...source };}Memory Management
Object Pooling
class ObjectPool<T> { private pool: T[] = []; private factory: () => T; private reset: (item: T) => void;
constructor( factory: () => T, reset: (item: T) => void, initialSize = 0 ) { this.factory = factory; this.reset = reset;
// Pre-populate pool for (let i = 0; i < initialSize; i++) { this.pool.push(this.factory()); } }
acquire(): T { return this.pool.pop() || this.factory(); }
release(item: T): void { this.reset(item); this.pool.push(item); }}
// Usageinterface Particle { x: number; y: number; velocity: number; active: boolean;}
const particlePool = new ObjectPool<Particle>( // Factory () => ({ x: 0, y: 0, velocity: 0, active: false }), // Reset (particle) => { particle.x = 0; particle.y = 0; particle.velocity = 0; particle.active = false; }, 1000 // Initial pool size);Memory-Efficient Data Structures
class CircularBuffer<T> { private buffer: T[]; private head = 0; private tail = 0; private size = 0;
constructor(private capacity: number) { this.buffer = new Array(capacity); }
push(item: T): void { this.buffer[this.tail] = item; this.tail = (this.tail + 1) % this.capacity; this.size = Math.min(this.size + 1, this.capacity); if (this.size === this.capacity) { this.head = (this.head + 1) % this.capacity; } }
pop(): T | undefined { if (this.size === 0) return undefined; const item = this.buffer[this.head]; this.head = (this.head + 1) % this.capacity; this.size--; return item; }}
// Usageconst messageBuffer = new CircularBuffer<string>(100);WeakMap for Memory Management
class Cache<K extends object, V> { private cache = new WeakMap<K, V>(); private hits = 0; private misses = 0;
get(key: K): V | undefined { const value = this.cache.get(key); if (value === undefined) { this.misses++; } else { this.hits++; } return value; }
set(key: K, value: V): void { this.cache.set(key, value); }
get stats() { const total = this.hits + this.misses; return { hits: this.hits, misses: this.misses, hitRate: total ? this.hits / total : 0 }; }}
// Usageconst resourceCache = new Cache<object, string>();Runtime Performance
Memoization
function memoize<T extends object, R>( fn: (arg: T) => R, keyFn: (arg: T) => string = JSON.stringify): (arg: T) => R { const cache = new Map<string, R>();
return (arg: T): R => { const key = keyFn(arg); if (cache.has(key)) { return cache.get(key)!; } const result = fn(arg); cache.set(key, result); return result; };}
// Usageinterface QueryParams { userId: string; filter: string;}
const expensiveQuery = memoize( (params: QueryParams) => { // Expensive computation return `result for ${params.userId}`; }, // Custom key function for better performance (params) => `${params.userId}:${params.filter}`);Lazy Evaluation
class LazyValue<T> { private value: T | undefined; private computed = false;
constructor(private factory: () => T) {}
get(): T { if (!this.computed) { this.value = this.factory(); this.computed = true; } return this.value!; }
invalidate(): void { this.computed = false; this.value = undefined; }}
// Usageconst expensiveOperation = new LazyValue(() => { // Expensive computation return complexCalculation();});Batch Processing
class BatchProcessor<T> { private batch: T[] = []; private timer: NodeJS.Timeout | null = null;
constructor( private process: (items: T[]) => Promise<void>, private options: { maxSize: number; maxWait: number; } ) {}
add(item: T): void { this.batch.push(item);
if (this.batch.length >= this.options.maxSize) { this.flush(); } else if (!this.timer) { this.timer = setTimeout(() => this.flush(), this.options.maxWait); } }
private async flush(): Promise<void> { if (this.timer) { clearTimeout(this.timer); this.timer = null; }
if (this.batch.length === 0) return;
const items = [...this.batch]; this.batch = [];
await this.process(items); }}
// Usageconst batchLogger = new BatchProcessor<string>( async (messages) => { await logToServer(messages); }, { maxSize: 100, maxWait: 1000 });Build Time Optimization
Project References
// tsconfig.base.json{ "compilerOptions": { "composite": true, "declaration": true, "declarationMap": true, "sourceMap": true }}
// packages/core/tsconfig.json{ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", "rootDir": "./src" }}
// packages/api/tsconfig.json{ "extends": "../../tsconfig.base.json", "references": [ { "path": "../core" } ], "compilerOptions": { "outDir": "./dist", "rootDir": "./src" }}Module Resolution
// ❌ Expensive module resolutionimport { something } from '../../../shared/utils';
// ✅ Path aliases for better performance// tsconfig.json{ "compilerOptions": { "baseUrl": ".", "paths": { "@shared/*": ["src/shared/*"], "@utils/*": ["src/utils/*"] } }}
// Usageimport { something } from '@shared/utils';Framework-Specific Optimizations
React Optimization
// ❌ Unnecessary rerendersconst Component = () => { const [data, setData] = useState<Data[]>([]);
// Created on every render const processedData = data.map(item => ({ ...item, processed: true }));
// New function on every render const handleClick = () => { // Handle click };
return ( <div onClick={handleClick}> {processedData.map(item => ( <Item key={item.id} data={item} /> ))} </div> );};
// ✅ Optimized componentconst Component = () => { const [data, setData] = useState<Data[]>([]);
// Memoized data processing const processedData = useMemo( () => data.map(item => ({ ...item, processed: true })), [data] );
// Memoized callback const handleClick = useCallback(() => { // Handle click }, []);
return ( <div onClick={handleClick}> {processedData.map(item => ( <Item key={item.id} data={item} /> ))} </div> );};Vue Optimization
// ❌ Expensive computations in template<template> <div> {{ expensiveComputation() }} <item v-for="item in items" :key="item.id" :processed-data="processItem(item)" /> </div></template>
// ✅ Optimized component<script setup lang="ts">import { computed, ref } from 'vue';
const items = ref<Item[]>([]);
// Computed property for expensive computationconst computedValue = computed(() => { return expensiveComputation();});
// Memoized item processingconst processedItems = computed(() => items.value.map(processItem));</script>
<template> <div> {{ computedValue }} <item v-for="item in processedItems" :key="item.id" :data="item" /> </div></template>These optimization techniques help you:
- Improve type system performance
- Reduce memory usage
- Enhance runtime performance
- Speed up build times
- Optimize framework-specific code
Would you like me to:
- Add more optimization techniques?
- Create posts about TypeScript testing strategies?
- Add more implementation details?