Skip to main content

Module 27: Performance Optimization

Learn performance optimization techniques for TypeScript applications including compilation, runtime performance, and bundle size reduction.


1. Compilation Performance

// tsconfig.json
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo",
"skipLibCheck": true,
"skipDefaultLibCheck": true
}
}

2. Tree Shaking

// utils.ts - Use named exports
export const add = (a: number, b: number) => a + b;
export const multiply = (a: number, b: number) => a * b;

// app.ts - Import only what you need
import { add } from "./utils"; // multiply will be tree-shaken

3. Code Splitting

// Dynamic imports
async function loadFeature() {
const module = await import("./feature");
module.initialize();
}

// React lazy loading
const Dashboard = React.lazy(() => import("./Dashboard"));

4. Memoization

function memoize<T extends (...args: any[]) => any>(fn: T): T {
const cache = new Map();

return ((...args: Parameters<T>): ReturnType<T> => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
}) as T;
}

const expensiveCalculation = memoize((n: number): number => {
console.log("Computing...");
return n * 2;
});

5. Lazy Evaluation

class LazyValue<T> {
private value?: T;
private computed = false;

constructor(private compute: () => T) {}

get(): T {
if (!this.computed) {
this.value = this.compute();
this.computed = true;
}
return this.value!;
}
}

const lazy = new LazyValue(() => {
console.log("Computing expensive value...");
return 42;
});

// Value computed only when accessed
console.log(lazy.get());

6. Object Pool Pattern

class ObjectPool<T> {
private available: T[] = [];
private inUse = new Set<T>();

constructor(
private factory: () => T,
private reset: (obj: T) => void,
initialSize: number = 10
) {
for (let i = 0; i < initialSize; i++) {
this.available.push(this.factory());
}
}

acquire(): T {
let obj = this.available.pop();
if (!obj) {
obj = this.factory();
}
this.inUse.add(obj);
return obj;
}

release(obj: T): void {
if (this.inUse.has(obj)) {
this.inUse.delete(obj);
this.reset(obj);
this.available.push(obj);
}
}
}

7. Debounce and Throttle

function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: NodeJS.Timeout;

return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}

function throttle<T extends (...args: any[]) => any>(
fn: T,
limit: number
): (...args: Parameters<T>) => void {
let inThrottle: boolean;

return (...args: Parameters<T>) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}

8. Virtual Scrolling

interface VirtualScrollProps<T> {
items: T[];
itemHeight: number;
containerHeight: number;
renderItem: (item: T) => React.ReactNode;
}

function VirtualScroll<T>({ items, itemHeight, containerHeight, renderItem }: VirtualScrollProps<T>) {
const [scrollTop, setScrollTop] = useState(0);

const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight),
items.length
);

const visibleItems = items.slice(startIndex, endIndex);
const totalHeight = items.length * itemHeight;
const offsetY = startIndex * itemHeight;

return (
<div
style={{ height: containerHeight, overflow: "auto" }}
onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}
>
<div style={{ height: totalHeight, position: "relative" }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map(renderItem)}
</div>
</div>
</div>
);
}

9. Worker Threads

// worker.ts
self.onmessage = (e: MessageEvent) => {
const result = expensiveCalculation(e.data);
self.postMessage(result);
};

// main.ts
const worker = new Worker("worker.js");

worker.postMessage(data);
worker.onmessage = (e) => {
console.log("Result:", e.data);
};

10. Bundle Analysis

# Webpack Bundle Analyzer
npm install --save-dev webpack-bundle-analyzer

# Run analysis
npx webpack-bundle-analyzer dist/stats.json

Key Takeaways

Incremental compilation speeds up builds
Tree shaking removes unused code
Code splitting reduces initial load
Memoization caches results
Virtual scrolling for large lists
Worker threads for CPU-intensive tasks


Practice Exercises

Exercise 1: Implement LRU Cache

class LRUCache<K, V> {
private cache = new Map<K, V>();

constructor(private capacity: number) {}

get(key: K): V | undefined {
// Implementation
}

set(key: K, value: V): void {
// Implementation
}
}

Exercise 2: Performance Monitoring

Create a performance monitoring decorator for functions.


Next Steps

In Module 28, we'll explore Error Handling Best Practices for robust TypeScript applications.