Avatar

Nǐ hǎo, I'm Julia.

Type Aliases and Interfaces

#typescript

3 min read

I remember being pretty confused about when to use types aliases (more commonly referred to simply as “types”) vs interfaces when I first started programming with Typescript. I therefore thought I’d write this post to help others who might be wondering the same thing. 😉

Types aliases

Type aliases allow us to reuse type definitions, much like you’d define variables using const. Similarly to const, a type alias can only be declared once within a scope.

Aliases allow us to:

  • Make our code more readable by allowing us to name our types. Convention is to use TitleCase.
  • Only have to define our type once (DRY principle) and reuse as needed by importing and exporting modules.

Inheritance using type aliases

Whilst there’s no true concept of inheritance with aliases, you can create a new type alias that combines a new type with an existing type by using the intersection operator (&).

type NintendoSwitchGame = ConsoleGame & { getPriceFromSwitchStore(): number }

const marioKart: NintendoSwitchGame = {
  ...new ConsoleGame(),
  getPriceFromSwitchStore: () => 49.99,
}

// marioKart will have all the types of the ConsoleGame class, as well as the getPriceFromSwitchStore prop.

Interfaces

Interfaces are a way of defining an object type (similar to what you might consider an instance of a class looking like). As these are specifically objects, union types cannot be interfaces. However, like type aliases, interfaces can be defined once and used where needed by importing and exporting modules.

Another core difference is that unlike type aliases, interfaces can have multiple declarations within the same scope. These declarations will be merged together into one (as if they had been declared singly - this makes interfaces open).

window.document // already exists in your code base

// We need to tell TypeScript that we want to add a new prop to the global Window object
interface Window {
  juliasnewprop: string
}

window.juliasnewprop = 'bionicjulia' // Window.juliasnewprop: string

Inheritance using interfaces

The inheritance concept can be achieved by using Typescript’s heritage clauses, extends and implements.

  1. Using extends - this is very similar to how inheritance works in JavaScript whereby a child class extends from a parent class.
class ConsoleGame {
  startGame(name) {
    // ...
  }
}

class OculusVRGame extends ConsoleGame {
  confirmEntryIntoVRUniverse() {
    // ...
  }
}

const miniGolfVR = new OculusVRGame()

// both props are accessible
miniGolfVR.startGame
miniGolfVR.confirmEntryIntoVRUniverse
  1. Using implements - used to declare that a given class should return instances that confirm to a specific interface.
interface ConsoleGameInterface {
  startGame(name): void
}

// Typescript will complain here with the following error:
// Class 'OculusVRGame' incorrectly implements interface 'ConsoleGameInterface'.
// Property 'startGame' is missing in type 'OculusVRGame' but required in type 'ConsoleGameInterface'.
class OculusVRGame implements ConsoleGameInterface {
  confirmEntryIntoVRUniverse() {
    // ...
  }
}

Side note: both implements and extends can be used together.

When to use type aliases vs interfaces

Both can be used interchangeably in most situations, however:

  • Use a type aliases if you need to define something that is not an object type (e.g. if you need to use union types (|)).
  • Use an interface if you need to use implements or extends.

Am I missing anything? Let me know and I’ll add it here.

© 2016-2024 Julia Tan · Powered by Next JS.