Introduction: Why Can TypeScript Be Slow?
TypeScript is a powerful tool that adds static typing to JavaScript. However, developers often find that "out of the box," TypeScript applications run slower than pure JS. The main reasons are excessive transpilation, incorrect compiler configuration, and suboptimal code patterns. In this article, we will cover practical optimization methods that will help speed up both the build process and the runtime of your application.
1. Profiling and Finding Bottlenecks
Before optimizing anything, you need to understand what is causing the slowdown. Use built-in browser and Node.js tools.
1.1. Chrome DevTools Performance
Record a profile of your application's execution. Pay attention to Long Tasks and frequent garbage collector (GC) calls. TypeScript code after compilation is regular JavaScript, so all standard profiling methods work.
1.2. Analyzing Compilation Time (tsc --generateTrace)
If the compilation itself is slow, use the --generateTrace flag:
tsc --generateTrace trace_outputThen open the trace_output/trace.json file in Chrome at chrome://tracing. You will see which files take the longest to compile. These are often large libraries or files with excessive imports.
2. Configuring tsconfig.json for Maximum Speed
Incorrect configuration is the main enemy of performance. Let's look at key options.
2.1. strict: true — Don't Rush
The strict mode enables all strict type checks. This slows down compilation. For production builds, use:
{ "compilerOptions": { "strict": false, "noImplicitAny": true, "strictNullChecks": true }}These two options cover 90% of errors but work faster than full strict.
2.2. Options for Faster Builds
incremental: true— enables incremental compilation. TypeScript saves information about the previous build and recompiles only changed files.skipLibCheck: true— disables type checking in declaration files (.d.ts). This provides a significant speed boost, especially when using many third-party libraries.isolatedModules: true— allows using faster transpilers (e.g., esbuild) instead of tsc.
Example of an optimized configuration for development:
{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "node", "incremental": true, "skipLibCheck": true, "strict": false, "noImplicitAny": true, "strictNullChecks": true, "outDir": "./dist" }, "include": ["src/**/*"], "exclude": ["node_modules", "**/*.test.ts"]}3. Runtime Optimization: Types and Data Structures
TypeScript types disappear after compilation, but their "imprint" remains in the form of code generated by the compiler.
3.1. Avoid Complex Conditional Types at Runtime
Complex conditional types can generate a lot of code. Instead, use simple union types:
// Bad: complex conditional typetype ResponseType<T> = T extends string ? { data: string } : { data: number };function handleResponse<T>(value: T): ResponseType<T> { ... }
// Good: simple uniontype ResponseType = { data: string } | { data: number };function handleResponse(value: string | number): ResponseType { ... }3.2. Use as const for Constants
This helps TypeScript infer more precise types, which reduces the number of checks:
const COLORS = ['red', 'green',