Profiling TypeScript Code: Finding Bottlenecks in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Profiling TypeScript Code: Finding Bottlenecks in Practice



When your TypeScript project starts to slow down and users complain about delays, it's time for profiling. Profiling is not just "seeing what's slow," but a systematic approach to identifying performance bottlenecks in code. In this article, we will explore practical methods for profiling TypeScript code with real examples and tools.



Why Profile TypeScript Code?



TypeScript compiles to JavaScript, and all performance issues inherent to JS carry over to TS. However, TypeScript adds its own nuances: typing, decorators, and generics can create unexpected overhead. Profiling helps answer questions like:

  • Which function takes the longest to execute?
  • Where is a memory leak occurring?
  • Why is the animation lagging at 60fps?
  • Which algorithm needs optimization?


Tools for Profiling TypeScript



Chrome DevTools (for browser code)

The most powerful tool for frontend. Open the Performance tab, click Record, perform an action, and stop the recording. You will see a flamegraph — a visualization of function calls and their execution time.



// Example of slow code for profilingfunction heavyCalculation(iterations: number): number {    let result = 0;    for (let i = 0; i < iterations; i++) {        result += Math.sqrt(i) * Math.sin(i);    }    return result;}

// Run in the browser console, then profileheavyCalculation(1000000);


Node.js — Built-in Profiler

For server-side code, use the --inspect flag with Chrome DevTools, or the built-in --prof to generate logs.



// server.tsimport express from 'express';

const app = express();

app.get('/heavy', (req, res) => { const data = processData(10000); res.json({ result: data });});

function processData(size: number): number[] { const arr: number[] = []; for (let i = 0; i < size; i++) { arr.push(i * Math.random()); } return arr.sort((a, b) => a - b);}

app.listen(3000);// Run: node --prof dist/server.js


Benchmark.js for Precise Measurements

When you need to compare two implementations, use the benchmark library.



import Benchmark from 'benchmark';

const suite = new Benchmark.Suite();

// Slow variantfunction slowSearch(arr: number[], target: number): boolean { for (let i = 0; i < arr.length; i++) { if (arr[i] === target) return true; } return false;}

// Fast variant with Setfunction fastSearch(arr: number[], target: number): boolean { const set = new Set(arr); return set.has(target);}

const testArray = Array.from({ length: 10000 }, (_, i) => i);

suite .add('slowSearch', () => slowSearch(testArray, 9999)) .add('fastSearch', () => fastSearch(testArray, 9999)) .on('cycle', (event: any) => console.log(String(event.target))) .on('complete', function(this: any) { console.log('Fastest is ' + this.filter('fastest').map('name')); }) .run({ async: true });


Practical Examples of Finding Bottlenecks



Example 1: Memory Leak via Closures

A typical problem in React applications — closures holding references to large objects.



// Problematic codefunction createHeavyComponent() {    const hugeData = new Array(1000000).fill('data');        return {        getData: () => hugeData, // closure holds hugeData        updateData: (newData: string) => {            // mutation, but the reference remains            hugeData.push(newData);        }    };}

const component = createHeavyComponent();// hugeData will not be garbage collected as long as component exists


<

Blogs

Book Recommendations