Back to Blog
TypeScript
8 min read2024-09-15

TypeScript Best Practices

Yannis Raftopoulos
Yannis Raftopoulos
TypeScript Best Practices

TypeScript Best Practices

TypeScript has become the standard for large-scale JavaScript applications. Here are some best practices to help you write more maintainable and robust TypeScript code.

Type Definitions

Use Explicit Types

Be explicit about your types when TypeScript's inference isn't clear:

// Good
const users: User[] = getUsers();

// Avoid when type is complex
const users = getUsers();

Leverage Interfaces and Types

Use interfaces for object shapes and types for unions, intersections, and primitives:

// Object shape - use interface
interface User {
  id: number;
  name: string;
  email: string;
}

// Union type - use type
type Status = 'pending' | 'approved' | 'rejected';

Readonly Properties

Use readonly for properties that shouldn't change:

interface Config {
  readonly apiUrl: string;
  readonly timeout: number;
}

Function Types

Define Return Types

Explicitly define function return types for better documentation and error catching:

function getUser(id: number): User | undefined {
  // Implementation
}

Use Function Type Expressions

Define reusable function types:

type ErrorHandler = (error: Error) => void;

function registerErrorHandler(handler: ErrorHandler) {
  // Implementation
}

Advanced Types

Discriminated Unions

Use discriminated unions for type-safe handling of different shapes:

type Action =
  | { type: 'INCREMENT'; payload: number }
  | { type: 'DECREMENT'; payload: number }
  | { type: 'RESET' };

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case 'INCREMENT':
      return state + action.payload;
    case 'DECREMENT':
      return state - action.payload;
    case 'RESET':
      return 0;
  }
}

Utility Types

Leverage TypeScript's utility types:

// Make all properties optional
type PartialUser = Partial<User>;

// Make all properties required
type RequiredUser = Required<User>;

// Extract a subset of properties
type UserCredentials = Pick<User, 'email' | 'password'>;

// Omit certain properties
type PublicUser = Omit<User, 'password'>;

Project Configuration

Strict Mode

Enable strict mode in your tsconfig.json:

{
  "compilerOptions": {
    "strict": true
  }
}

Organize Imports

Use import organization and consistent naming:

// Group imports
import React, { useState, useEffect } from 'react';
import { User, Role } from './types';
import { fetchUsers } from './api';

// Use consistent naming
import * as UserAPI from './user-api';

By following these best practices, you'll write TypeScript code that's more maintainable, less prone to bugs, and easier for other developers to understand.