TypeScript
  • Introduction
  • Introduction
    • What is TypeScript?
    • Why TypeScript?
    • Setup
  • Beginner
    • Basic types
      • Primitive types
      • Arrays and tuples
      • Enums
      • Any type
    • Objects
    • Type aliases
    • Interfaces
    • Functions
      • Function signatures
      • Void type
      • Functions as types
    • Union types
    • Type guards
    • Intersection types
    • Nullable types
    • Inference
  • Resources
    • Resources
Powered by GitBook
On this page
  • Interfaces are extensible
  • Optional Properties
  • Interfaces in classes
  • Interfaces vs. type aliases
  1. Beginner

Interfaces

Interfaces provide a powerful way to enforce the structure of variables. As you'll recall, TypeScript relies on duck typing, which makes interfaces a key way to enforce that an object (or class) adhere to a certain schema, or contract, as defined in the interface.

Consider the following example. The function getRectangleArea accepts anything so long as it contains length and height properties:

interface Rectangle {
  length: number;
  height: number;
}

interface Cube {
  length: number;
  height: number;
  width: number;
}

const myRectangle: Rectangle = {
  length: 10,
  height: 10,
};

const myCube: Cube = {
  length: 10,
  height: 10,
  width: 2,
};

function getRectangleArea(rectangle: Rectangle) { /* do stuff */ }

getRectangleArea(myRectangle); // exact match is ideal
getRectangleArea(myCube); // extra properties are okay
getRectangleArea({ // Error: missing property `height`
  length: 10,
});
interface Coords {
  latitude: number;
  longitude: number;
}

interface City {
  coords: Coords;
  name: string;
}

const userLocation: City = {
  coords: {
    latitude: 42.332,
    longitude: -73.324,
  },
  name: 'Montréal',
};

Because interfaces operate entirely at TypeScript compile time, they have zero impact at JS runtime.

The above example will simply transpile down to this JavaScript:

var userCity = {
  coords: {
    latitude: 42.332,
    longitude: -73.324,
  },
  name: 'Montréal',
};

✨ ✨ ✨ ✨ ✨ ✨

Interfaces are extensible

Interfaces in TypeScript can be extended if necessary. Simply re-declare the interface with additional properties. The interface's properties will then be merged with any that were previously declared.

For example:

interface Squirrel {
  id: number;
  mutations: string[];
}

interface Squirrel {
  scientificName: string;
}

will effectively be enforced in the same way as

interface Squirrel {
  id: number;
  mutations: string[];
  scientificName: string;
}

This can be useful, for example, if an external library defines an interface that needs to be augmented.

Optional Properties

Not all properties of an interface may be required. These optional properties are popular when creating patterns like "option bags" where you pass an object to a function that only has a couple of properties filled in.

For example:

interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
  const newSquare = {
    color: 'white',
    area: 100
  };

  if (config.color) {
    newSquare.color = config.color;
  }

  if (config.width) {
    newSquare.area = config.width * config.width;
  }

  return newSquare;
}

let redSquare = createSquare({color: 'red'}); // {color: 'red', area: 100}

Interfaces in classes

Interfaces can also be used to enforce classes to adhere to a certain contract.

For example:

interface Dog {
  species: string;
  wagTail(speed: number): void;
}

class MyDog implements Dog {
  species = 'Canis familaris';
  isCool = true;
  wagTail(speed: number) { /* wag tail logic */};
}

To those unfamiliar, this might look like superfluous boilerplate. However, a lot of value is gained in the form of trust if you can make guarantees about what structure a class should have. In other words, MyDog may be more, but for the public API it must at least be a Dog.

Interfaces vs. type aliases

  • Interfaces create a new name that is used everywhere. Type aliases don’t create a new name — for instance, error messages won’t use the alias name.

  • A second more important difference is that type aliases cannot be extended or implemented from (nor can they extend/implement other types). Because an ideal property of software is being open to extension, you should always use an interface over a type alias if possible.

  • On the other hand, if you can’t express some shape with an interface and you need to use a union or tuple type, type aliases are usually the way to go.

PreviousType aliasesNextFunctions

Last updated 6 years ago