Interfaces or Type Aliases? Oh, my!

Prologue: Type unions and intersections

A | B is called a type union.

type StringOrNumber = string | number
var subject: StringOrNumber
subject = 'hello' // OK
subject = 2 // OK
subject = true // error

A & B is called a type intersection. It is mostly commonly used to merge object types .

type Person = { name: string }
type Callable = { phone: string }
type CallablePerson = Person & Callable
var person: CallablePerson = { name: 'John' } // error, missing "phone"

Interfaces or Type Aliases? Oh, my!

  • Which one to use? Whatever. Both declare types!

  • Type aliases can receive other things than objects; It can receive any declared type. Most noticeable exclusive to those are:

    • Type unions and intersections;

    • Conditional types;

  • Interfaces work exclusively with objects (functions are also objects!). Exclusive to interfaces are:

    • The OOPish extends clause, which is somewhat similar to the type union of two objects;

    • Declaration merging. When you declare 2 interfaces with the same name, instead of clashing, their properties will merge. (They can still clash if their properties are incompatible, of course);

    • Common use of declaration merging: Add another property to the global DOM's Window declaration.

interface Animal {
    name: string
    isDomestic?: boolean  // optional property, receives type boolean|undefined
    readonly sciName: string  // forbids mutation. Notable sample: react's state
    yell(volume: 1 | 2 | 3 ): void
      //  - types can receive constants (1 | 2 | 3)
      //  - the "void" type is mostly only used in function returns, and
      //    has subtle differences from undefined
    (): void
      // declare this object as "callable" (functions are objects)
    new (): Animal
      // declare this object as "newable"
}

interface Cat extends Animal {
    isDomestic: true   // narrows down parent's `isDomestic`
    meow(): void;      // additional property
}

// merges with the interface above
interface Cat extends Animal {
    purr(): void
}

const cat: Cat
cat.purr() // ok
cat.name // ok, comes from animal

Type alias sample below. Almost the same capabilities and syntax.

type SomeCallback = (i: string) => number
type DiscriminatedUnion = { type: 'a', data: number } | { type: 'b', data: string }

type Animal = {
    name: string
    isDomestic?: boolean
    readOnly sciName: string
    yell(volume: 1 | 2 | 3 ): void
    (): void
    new (): Animal
}

type Cat = Animal & {
    isDomestic: true
    meow(): void
}

// declaration merging not possible

Last updated