Bits Kingdom logo with a hexagon lattice, uppercase text in white, and a minimalistic design.

Strictly Typed: TypeScript From Zero to Production-Ready Code

Chapter 2: Interfaces and type aliases, the power of structural typing

by Jul 28, 2025Development

Home / Development / Strictly Typed: TypeScript From Zero to Production-Ready Code

As someone who’s spent countless hours developing with TypeScript, I’ve come to appreciate just how powerful and flexible its type system truly is. Interfaces and type aliases are essential tools that help me write clear, maintainable, and bug-resistant code. In fact, once you get used to describing your data precisely with TypeScript, working without these features feels like coding in the dark.

In this chapter, we’ll explore the fundamental differences between interfaces and type aliases, discover how structural typing makes TypeScript incredibly versatile, and understand why type compatibility is critical in everyday development.

3D TypeScript logo for a series of posts.

1. Interfaces: Defining Clear Contracts

Interfaces allow you to explicitly outline the shape of objects and classes, setting clear expectations for your code.

Basic Example:

interface User {
  id: number;
  name: string;
  isActive: boolean;
}

const user1: User = {
  id: 1,
  name: "Ada",
  isActive: true,
};

Optional Properties and Readonly

interface UserProfile {
  readonly id: number;
  name: string;
  email?: string; // optional
}

Extending Interfaces
Interfaces can extend other interfaces, making your types reusable and flexible.

interface Person {
  name: string;
}
interface Employee extends Person {
  employeeId: number;
}

const staff: Employee = { name: "Grace", employeeId: 123 };

Describing Function Types

interface Greeter {
  (name: string): string;
}

const greet: Greeter = (name) => `Hello, ${name}`;

2. Type Aliases: More Than Just Objects

Type aliases can represent a wide variety of types—including primitives, unions, tuples, intersections, and utilities—offering unmatched flexibility in your codebase.

Basic Example:

type UserID = number | string;
let id1: UserID = 42;
let id2: UserID = "user_42";

Type Aliases for Objects

type Point = { x: number; y: number };
const pt: Point = { x: 1, y: 2 };

Union and Intersection Types

type Success = { status: "success"; data: string };
type Failure = { status: "error"; error: string };
type Result = Success | Failure;

type Named = { name: string };
type Timestamped = { timestamp: Date };
type NamedEvent = Named & Timestamped;

3. Interface vs. Type Alias: Key Differences

FeatureInterfaceType Alias
Objects/classesYesYes
Primitives/UnionsNoYes
Extends/implementsYesLimited
Declaration mergingYesNo
  • Interfaces: Ideal for outlining object shapes and class implementations.
  • Type Aliases: Excel in defining unions, intersections, primitives, and more complex type compositions.

Note: While interfaces and type aliases can often serve similar purposes for object definitions, interfaces are generally preferred unless the flexibility of type aliases is specifically required.

4. Structural Typing: TypeScript’s Secret

TypeScript employs structural typing (also known as “duck typing”). This means two types are considered compatible if their structures match, regardless of their original declarations.

Example:

interface Pet { name: string }
type Animal = { name: string }

const dog: Pet = { name: "Rex" };
const cat: Animal = dog; // No error!

Here, Pet and Animal are compatible because their shapes match.

5. Type Compatibility in Action

In TypeScript, compatibility is all about matching structure, not type names, simplifying and streamlining your code interactions.

interface A { x: number }
interface B { x: number; y: number }

const b: B = { x: 5, y: 10 };
const a: A = b; // This is fine; B has at least the properties of A

But not the other way around:

const a2: A = { x: 5 };
const b2: B = a2; // Error: missing 'y'

6. Practical Examples

Combining Types with Intersection

type WithId = { id: number };
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type Entity = WithId & WithTimestamps;

Defining API Responses

type ApiResponse = { status: number; body: any };

Extending 3rd-party Types

interface Window {
  myCustomProperty?: string;
}

Quick Recap

  • Interfaces: Clearly define the structure and contracts for objects and classes.
  • Type Aliases: Offer powerful flexibility for complex type definitions.
  • Structural Typing: Emphasizes matching shapes over explicit type names—if it looks like a duck, it’s a duck!
  • Compatibility: TypeScript checks are based on structure, simplifying type management.

Next up, we’ll tackle Mastering Functions, Generics, and Elegant Code Reuse, enhancing your ability to write sophisticated, reusable TypeScript code.

About the author

<a href="https://bitskingdom.com/blog/author/thomas/" target="_self">Thomas Barreto</a>
Thomas Barreto
I am a Full Stack Developer with over 3 years of experience, specializing in frontend. I’m passionate about understanding how things work and always looking for ways to improve them. I enjoy facing new challenges, learning from every experience, and achieving results as part of a team.

Explore more topics:

E-commerce That Lasts: 5 Technical Secrets to Future-Proof Your Store

Discover Strategies to Build a Site That Scales, Secures, and Sells.