TypeScript Type Aliases

Type aliases create a new name for an existing type, making your TypeScript code more readable, maintainable, and reusable. They don't create new types but instead provide a way to refer to types with more descriptive names and encapsulate complex type definitions.

Basic Type Alias Syntax

The basic syntax for creating a type alias uses the type keyword:

Basic Type Alias

type UserId = number;

// Using the type alias
const id: UserId = 123456;

This simple example might not seem very useful, but type aliases become powerful when you work with more complex types:

More Useful Type Alias

type Coordinates = {
  x: number;
  y: number;
};

// Using the type alias
function calculateDistance(point1: Coordinates, point2: Coordinates): number {
  return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
}

const start: Coordinates = { x: 0, y: 0 };
const end: Coordinates = { x: 3, y: 4 };

console.log(calculateDistance(start, end)); // 5

Union Types

One of the most common and powerful uses of type aliases is for creating union types, which allow a value to be one of several types:

Union Types

type ID = string | number;

function printID(id: ID) {
  console.log(`ID: ${id}`);

  // Type narrowing
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(0));
  }
}

printID(101); // Valid
printID("202"); // Also valid
// printID(true); // Error: Type 'boolean' is not assignable to type 'ID'

Intersection Types

Intersection types combine multiple types into one, enabling an object to have all properties from each type:

Intersection Types

type Person = {
  name: string;
  age: number;
};

type Employee = {
  employeeId: string;
  department: string;
};

type EmployeeRecord = Person & Employee;

const employee: EmployeeRecord = {
  name: "John Smith",
  age: 35,
  employeeId: "E123",
  department: "Engineering",
};

Literal Types

Type aliases can be used with literal types to create a type that can only be one of specific values:

Literal Types

type Direction = "North" | "East" | "South" | "West";

function move(direction: Direction, steps: number) {
  console.log(`Moving ${steps} steps to the ${direction}`);
}

move("North", 3); // Valid
// move("Up", 3);    // Error: Argument of type '"Up"' is not assignable to parameter of type 'Direction'

Type Aliases for Functions

Type aliases can describe function signatures:

Function Type Aliases

type MathOperation = (a: number, b: number) => number;

const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;
const multiply: MathOperation = (a, b) => a * b;
const divide: MathOperation = (a, b) => a / b;

function performOperation(a: number, b: number, operation: MathOperation): number {
  return operation(a, b);
}

console.log(performOperation(10, 5, add)); // 15
console.log(performOperation(10, 5, subtract)); // 5

Type Aliases vs. Interfaces

While type aliases and interfaces can be used in similar ways for defining object shapes, they have some key differences:

Type Aliases vs. Interfaces
FeatureType AliasesInterfaces
Declaration MergingNoYes
Extends ClauseNo (but can use intersection &)Yes (via extends)
Union TypesYesNo
Primitive TypesYesNo
TuplesYesNo

When to Use Type Aliases

Type aliases are preferable in these scenarios:

  1. When working with union types or literal types:

    type Result = Success | Error;
    type HttpStatus = 200 | 201 | 400 | 404 | 500;
    
  2. When you need a new name for an existing type:

    type UserId = string;
    
  3. When working with tuples:

    type Point = [number, number];
    
  4. When you're creating function signatures:

    type Callback = (data: string) => void;
    type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
    
  5. When using primitive types:

    type Age = number;
    type Email = string;
    

Real-World Examples

API Response Types

Type aliases are excellent for modeling API responses and ensuring type safety:

API Response Types

type User = {
  id: number;
  username: string;
  email: string;
};

type ApiResponse = {
  data: User;
  status: number;
  message: string;
  timestamp: string;
};

// Using the types
async function fetchUser(id: number): Promise<ApiResponse> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// Usage
fetchUser(123).then((response) => {
  if (response.status === 200) {
    const user = response.data;
    console.log(`Found user: ${user.username}`);
  }
});

State Management

Type aliases help manage state in applications:

State Management Types

// Define the structure of our state
type User = {
  id: string;
  name: string;
  email: string;
};

type AuthState = {
  isAuthenticated: boolean;
  user: User | null;
  token: string | null;
  error: string | null;
};

// Initial state
const initialState: AuthState = {
  isAuthenticated: false,
  user: null,
  token: null,
  error: null,
};

// Function to update state
function login(state: AuthState, user: User, token: string): AuthState {
  return {
    ...state,
    isAuthenticated: true,
    user: user,
    token: token,
    error: null,
  };
}

// Usage
const updatedState = login(
  initialState,
  {
    id: "123",
    name: "John Doe",
    email: "john@example.com",
  },
  "auth-token-123"
);

Best Practices for Type Aliases

1. Use PascalCase for naming type aliases:

type UserProfile = { ... }; // Good
type userProfile = { ... }; // Not ideal

2. Be specific with your type names:

// Too vague
type Data = { ... };

// More specific and clearer
type UserData = { ... };

3. Use type aliases for complex or reused types:

// Define once, use many times
type Coordinates = {
  x: number;
  y: number;
};

function calculateDistance(point1: Coordinates, point2: Coordinates): number { ... }
function movePoint(point: Coordinates, direction: string, distance: number): Coordinates { ... }

4. Use interfaces for object types that might be extended or implemented:

// Better as an interface if it might be extended
interface ApiClient {
  get(url: string): Promise<unknown>;
  post(url: string, data: unknown): Promise<unknown>;
}

// Better as a type alias
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";

5. Document your types with JSDoc:

/**
 * Represents a response from the API
 */
type ApiResponse = {
  /** The returned data */
  data: unknown;
  /** HTTP status code */
  status: number;
  /** Success or error message */
  message: string;
};

Exercises

Exercise 1: Basic Type Aliases

Create a few basic type aliases:

  • A type alias for a string ID
  • A type alias for a coordinate with x and y properties
  • A type alias for a configuration object with properties apiUrl (string), timeout (number), and debug (boolean)

Exercise 2: Union and Literal Types

Create a type alias for a Status that can only be one of these string literals: "pending", "processing", "completed", "failed".

Then, create a function called updateStatus that takes an ID and a Status as parameters, and returns a message confirming the update.

Exercise 3: Intersection Types

Create two separate type aliases:

  1. Person with properties name (string), age (number)
  2. Worker with properties jobTitle (string), employeeId (string)

Then, create a third type alias Employee that is an intersection of Person and Worker. Create a sample Employee object.