Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this happen in almost every codebase I've joined. A developer starts with a simple object, and as the feature grows, that object shape gets passed around to five different functions. It looks something like this:

function createUser(id: number, name: string, email: string) {
  return { id, name, email };
}

function sendWelcomeEmail(user: { id: number, name: string, email: string }) {
  console.log(`Sending email to ${user.email}...`);
}

function updateProfile(user: { id: number, name: string, email: string }, newName: string) {
  return { ...user, name: newName };
}

The maintenance trap of inline types

At first, this seems fine. But then, the product manager asks you to add a phoneNumber field to the user. You update createUser and sendWelcomeEmail, but you forget about updateProfile because it's buried in another file. Suddenly, TypeScript starts screaming at you—or worse, you have a runtime bug because the data flowing into updateProfile doesn't match what the function expects.

Repeating the same object literal { id: number, name: string, email: string } everywhere is a recipe for disaster. It's tedious to write, and it's a nightmare to maintain. If you change the type in one place, you have to hunt down every single instance of that shape across your entire project.

Giving your shapes a name

This is where type aliases come in. Instead of defining the shape inline every time, we create a "nickname" for that type using the type keyword. It doesn't create any actual JavaScript code when it compiles; it's purely a label for the TypeScript compiler to use.

type User = {
  id: number;
  name: string;
  email: string;
  phoneNumber?: string; // Now we can add this in one place
};

function createUser(id: number, name: string, email: string): User {
  return { id, name, email };
}

function sendWelcomeEmail(user: User) {
  console.log(`Sending email to ${user.email}...`);
}

function updateProfile(user: User, newName: string): User {
  return { ...user, name: newName };
}

Now, the User type is the single source of truth. If the requirements change again, you change it once in the alias, and every function using User is automatically updated. It makes the function signatures much cleaner and easier to read at a glance.

Handling "this or that" with Union Aliases

Type aliases aren't just for objects. In fact, one of the most powerful ways I use them is for union types. Imagine you're building a system that handles different types of notifications. You might have a status that can only be one of three specific strings.

Writing status: 'pending' | 'approved' | 'rejected' everywhere is clunky. Instead, do this:

type RequestStatus = 'pending' | 'approved' | 'rejected';

type Notification = {
  id: string;
  message: string;
  status: RequestStatus;
};

const myNote: Notification = {
  id: '123',
  message: 'Your request is being reviewed',
  status: 'pending' // TypeScript will autocomplete this for you!
};

I personally love this because it turns "magic strings" into a formal type. If you typo 'pendng', TypeScript catches it immediately. Without the alias, you're just guessing and hoping you spelled the string correctly in every single file.




📋 Practical Task

Refactoring the E-Commerce Order System

You've inherited a messy piece of code for an e-commerce checkout. Currently, the types for Product and Order are defined inline, and the order status is repeated as a union of strings in multiple places. Your task is to refactor this using Type Aliases to make it maintainable.

// Current broken/messy state:
function calculateTotal(items: { id: string, price: number, quantity: number }[]) {
  return items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
}

function shipOrder(order: { id: string, items: { id: string, price: number, quantity: number }[], status: 'processing' | 'shipped' | 'delivered' }) {
  console.log(`Shipping order ${order.id}...`);
}

function updateOrderStatus(order: { id: string, items: { id: string, price: number, quantity: number }[], status: 'processing' | 'shipped' | 'delivered' }, newStatus: 'processing' | 'shipped' | 'delivered') {
  return { ...order, status: newStatus };
}

Requirements:

  • Create a Product type alias for the item shape.
  • Create an OrderStatus type alias for the status union.
  • Create an Order type alias that utilizes both Product and OrderStatus.
  • Update the three functions to use these aliases instead of the inline definitions.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.