Tackling TypeScript
Please support this book: buy it or donate
(Ad, please don’t block.)

11 What is a type in TypeScript? Two perspectives



What are types in TypeScript? This chapter describes two perspectives that help with understanding them.

11.1 Two questions for each perspective

The following two questions are important for understanding how types work and need to be answered from each of the two perspectives.

  1. What does it mean for myVariable to have the type MyType?

    let myVariable: MyType;
  2. How is UnionType derived from Type1, Type2, and Type3?

    type UnionType = Type1 | Type2 | Type3;

11.2 Perspective 1: types are sets of values

From this perspective, a type is a set of values:

  1. If myVariable has the type MyType, then all values that can be assigned to myVariable must be elements of the set MyType.

  2. The union type of the types Type1, Type2, and Type3 is the set-theoretic union of the sets that define them.

11.3 Perspective 2: type compatibility relationships

From this perspective, we are not concerned with values and how they flow when code is executed. Instead, we take a more static view:

Let’s consider the questions:

  1. If myVariable has the type MyType, then we can only assign values to it whose static types are assignment-compatible with MyType.

  2. How union types work is also determined via assignment compatibility. We have seen two relevant rules.

An interesting trait of TypeScript’s type system is that the same variable can have different static types at different locations:

// %inferred-type: any[]
const arr = [];

arr.push(123);
// %inferred-type: number[]
arr;

arr.push('abc');
// %inferred-type: (string | number)[]
arr;

11.4 Nominal type systems vs. structural type systems

One of the responsibilities of a static type system is to determine if two static types are compatible:

This often means checking if Src is a subtype of Trg. Two approaches for this check are (roughly):

The following code produces a type error in line A with nominal type systems, but is legal with TypeScript’s structural type system because class A and class B have the same structure:

class A {
  name = 'A';
}
class B {
  name = 'B';
}
const someVariable: A = new B(); // (A)

TypeScript’s interfaces also work structurally – they don’t have to be implemented in order to match:

interface Point {
  x: number;
  y: number;
}
const point: Point = {x: 1, y: 2}; // OK

11.5 Further reading