TypeScript Performance Optimization

TypeScript project performance optimization involves multiple aspects such as compilation speed, runtime performance, and code size.

This tutorial introduces common performance optimization techniques to help build more efficient TypeScript applications.


SVG Diagram: Performance Optimization Dimensions Background Title TypeScript Performance Optimization Dimensions Compilation Speed Compilation Speed Incremental Build | skipLibCheck Runtime Performance Runtime Performance Type Inference | Avoid any Bundle Size Bundle Size Tree Shaking | Lazy Loading Bottom Section: Optimization Strategies Key Optimization Strategies Strategy 1 skipLibCheck Skip Library Type Checking Strategy 2 incremental Incremental Compilation Strategy 3 Project References Project References

Why Performance Optimization is Needed

Although TypeScript provides a powerful type system, improper use can affect compilation speed and runtime performance.

Compiling large projects may take several minutes, seriously affecting the development experience.

The configurations and techniques introduced in this tutorial can significantly improve the performance of TypeScript projects.

Optimization Goals:Faster compilation speed, smaller bundle size, and higher runtime performance.


Compilation Configuration Optimization

Improve compilation speed by optimizing the tsconfig.json configuration.

tsconfig.json Optimization Configuration

{
    "compilerOptions": {
        // Enable incremental compilation, save last compilation info
        "incremental": true,

        // Skip type checking of node_modules
        // Greatly improve compilation speed
        "skipLibCheck": true,

        // Skip declaration file generation (only generate during final build)
        "noEmit": true,

        // Enable fast incremental builds
        "assumeChangesOnlyAffectDirectDependencies": true,

        // Execute in parallel
        "parallel": true,

        // Enable caching
        "tsBuildInfoFile": ".tsbuildinfo",

        // Files that don't need to be parsed
        "exclude": [
            "node_modules",
            "dist",
            "build",
            "**/*.test.ts"
        ]
    }
}

skipLibCheck:This is the most important optimization option, which can reduce compilation time by more than 50%.


Project References Optimization

Use project references to split large projects into smaller modules to achieve incremental compilation.

packages/utils/tsconfig.json

{
    // Inherit base configuration
    "extends": "../../tsconfig.base.json",

    "compilerOptions": {
        "composite": true,
        "outDir": "./dist",
        "declaration": true,
        "declarationMap": true
    },

    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
}

composite:When enabled, TypeScript will generate .tsbuildinfo files to speed up subsequent compilations.


Type Inference Optimization

Make full use of TypeScript's type inference and avoid over-annotation.

< h2 class="example">Example
// Bad practice: over-annotating types
const name: string = "Alice";
const age: number = 25;
const isActive: boolean = true;

// Good practice: leveraging type inference
const name = "Alice";
const age = 25;
const isActive = true;

// Function return types can be omitted (TypeScript infers them automatically)
function add(a: number, b: number) {
    return a + b;
}

// Use type inference for complex objects
const user = {
    id: 1,
    name: "Bob",
    email: "[email protected]"
};
// TypeScript will infer:
// { id: number; name: string; email: string }

// Only annotate explicitly when type inference is inaccurate
const elements: HTMLElement[] = [];

Reduce Annotations:TypeScript's type inference is already very intelligent, and in most cases explicit type annotations are not needed.


Avoid Using any

Using any loses the benefits of type checking and affects runtime performance.

Example

// Bad practice: using any
function processData(data: any): any {
    return data.value;
}

// Good practice: using unknown or specific types
function processData<T extends { value: string }>(data: T): string {
    return data.value;
}

// If you really don't know the type, use unknown
function parseJSON(json: string): unknown {
    return JSON.parse(json);
}

// Perform type checking when using it
const data = parseJSON('{"key": "value"}');
if (typeof data === "object" && data !== null) {
    const obj = data as { key: string };
    console.log(obj.key);
}

// Better approach: use generics
function identity<T>(value: T): T {
    return value;
}

const result = identity("hello");
console.log("Result: " + result);

unknown vs any:unknown is a type-safe any that requires type checking before use.


Interface vs Type Alias

Choose the appropriate type definition approach based on the scenario.

Example

// Interface: suitable for defining object types, supports declaration merging
interface User {
    id: number;
    name: string;
}

// Extending an interface
interface User {
    email: string;
}

// Type alias: suitable for union types, tuples, and function types
type ID = string | number;
type Status = "pending" | "success" | "error";
type Callback = (data: string) => void;

// Utility types usually use type
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;

// Performance consideration: interfaces generally compile faster than type aliases
// For simple object types, you can use interface
interface Point {
    x: number;
    y: number;
}

// For union types, use type
type Shape = Circle | Square | Triangle;

Selection Recommendations:Use interfaces for object types, type for union types, and type for function types.


Build Tool Optimization

Configure build tools to achieve optimal performance.

vite.config.ts

// Import vite
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// Vite configuration optimization
export default defineConfig({
    plugins: [react()],

    // Build optimization
    build: {
        // Enable code splitting
        rollupOptions: {
            output: {
                // Manual chunk splitting
                manualChunks: {
                    'vendor': ['react', 'react-dom'],
                    'utils': ['lodash', 'axios']
                }
            }
        },

        // Minify code
        minify: 'terser',

        // Generate sourcemap
        sourcemap: false,

        // Chunk size limit
        chunkSizeWarningLimit: 500
    },

    // Dev server optimization
    server: {
        // Enable hot module replacement
        hmr: {
            overlay: true
        }
    },

    // Optimize dependency resolution
    optimizeDeps: {
        include: ['react', 'react-dom'],
        exclude: ['some-large-library']
    }
});

Code Splitting:Use manualChunks to separate large libraries and reduce the main bundle size.


Tree Shaking

Configure modules to support Tree Shaking, eliminating unused code.

src/utils/index.ts

// Use ES module exports to support Tree Shaking
export function add(a: number, b: number): number {
    return a + b;
}

export function subtract(a: number, b: number): number {
    return a - b;
}

export function multiply(a: number, b: number): number {
    return a * b;
}

export function divide(a: number, b: number): number {
    if (b === 0) {
        throw new Error("Cannot divide by zero");
    }
    return a / b;
}

// Named exports are more conducive to Tree Shaking than default exports
// Wrong: export default prevents Tree Shaking
// export default { add, subtract, multiply, divide };

// Correct: use named exports
console.log("Utility module loaded");

Named Exports:Using named exports instead of default exports allows build tools to perform Tree Shaking more effectively.


Notes

  • skipLibCheck:Must be enabled in production
  • Incremental Compilation:Recommended to enable in development environment
  • Avoid any:Use unknown instead
  • Tree Shaking:Use ES modules and named exports

Best Practices:Set up optimization configurations at the early stage of development to avoid later refactoring.


Summary

TypeScript performance optimization involves multiple aspects.

  • Compilation Optimization:skipLibCheck、incremental、project references
  • Type Optimization:Leverage inference, avoid any, choose appropriate type definitions
  • Build Optimization:Code splitting, Tree Shaking, dependency optimization
  • Runtime Optimization:Type safety, generic constraints

Recommendations:Regularly check compilation time and bundle size, and continuously optimize project performance.

Other Extensions