Learn TypeScript - Interface, Type Alias, and Type Declarations
Episode 5 of 23

Learn TypeScript - Interface, Type Alias, and Type Declarations

This episode teaches how to name object shapes with interface and type alias. You'll learn optional properties, readonly, index signatures, the fundamental differences between interface and type, and when each is more appropriate.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

In episode 4 you combined types with unions and intersections. Episode 5 answers the question that's sure to come next: how do you name an object shape that's reused over and over? Retyping an object structure in every function makes the code noisy and prone to inconsistency.

The two main tools for this are interfaces and type aliases. They look similar and are often considered interchangeable, yet they have behavioral differences that determine the quality of a type API in large projects. This episode also covers type declarations for functions so callback patterns stay safe.

After this episode you'll be able to design clear object contracts, reuse them without duplication, and pick the right tool with reasons you can explain.

Interface for Object Shapes

Naming an Object Shape

An interface declares the shape of an object with a block of properties:

Basic interface
interface Pengguna {
    nama: string;
    umur: number;
    email?: string;
}

The declaration interface Pengguna creates a new type that requires every value to have nama and umur properties. A question mark after the property name makes it optional, so email can be omitted. TypeScript then checks structural conformance at every use site.

Readonly Properties and Index Signatures

Two property decorations that are used often:

readonly and index signature
interface Konfigurasi {
    readonly appId: string;
    [key: string]: string | number;
}

A readonly property locks the value after the object is created, so reassigning it produces an error. An index signature allows extra dynamic properties as long as they match the declared type. This pattern is common for configuration maps and objects whose data isn't known up front.

Type Alias: The More Flexible Alternative

A type alias uses the type keyword and can name any shape, not just objects:

Type alias for various shapes
type Id = string | number;
type Status = "aktif" | "nonaktif";
type Titik = { x: number; y: number };
type Callback = (err: Error | null) => void;

A type alias can name unions, literals, functions, even combinations of all of them. An interface can't name a union or a primitive. That's the first reason to choose type over interface.

Interface vs Type: When to Use Which

Declaration Merging

The most distinguishing difference between the two is the ability to merge declarations:

Interface declaration merging
interface Catatan {
    judul: string;
}
 
interface Catatan {
    isi: string;
}

The two interface Catatan blocks above will be merged into a single type with both judul and isi properties. A type alias with the same name instead produces a duplicate error. This merging is useful when adding properties to an external library's type without changing its original files.

Extends vs Intersection

An interface inherits with extends, while a type alias combines with &:

Extending shapes
interface Dasar {
    id: number;
}
 
interface User extends Dasar {
    nama: string;
}
 
type Item = Dasar & { harga: number };

The end result is nearly the same. The community rule of thumb: use interface for objects and public APIs that need declaration merging, and use type for unions, literals, and complex structures. When in doubt, start with interface because it's easier to extend.

Tip

For plain objects, interface and type behave identically in structural checking. Choosing one over the other is more about style and extensibility needs than about capability differences.

Type Declarations for Functions

Functions can also be declared as types so callback patterns stay consistent across many places:

Function type declaration
type Penerima = (pesan: string, kode: number) => void;
 
function kirim(penerima: Penerima): void {
    penerima("selesai", 200);
}

Parameters and the return value are written out fully in the declaration. The variable penerima can only hold a function that takes a string and a number and returns void. TypeScript will reject callbacks whose signature differs from the contract.

Closing

Episode 5 equipped you with tools for naming types: interfaces for objects that need merging, and type aliases for everything expressive. You also learned property decorations and function declarations so data contracts are documented by the compiler.

Key takeaways:

  • Interfaces name object shapes and support declaration merging.
  • readonly properties prevent reassignment; index signatures hold dynamic properties.
  • Type aliases can name unions, literals, and function types.
  • Interfaces inherit with extends; type aliases combine with &.
  • Use interface for public objects, type for unions and complex shapes.
  • Function declarations enforce callback contracts across the codebase.

In the next episode 6 we'll discuss array, tuple, and enum types — how to model collections of data safely, fixed-length value pairs, and the literal alternative for enumerations.