TypeScript Type System: Union Types

Many JavaScript libraries support taking in values of more than one type. For example, jQuery’s height and width properties take an integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string).

Without union types, TypeScript definition files must represent this property as type any, effectively losing type safety. This shortcoming could be worked around in function overloads as TypeScript supports function overloads (multiple functions with same name but different argument types). However, there is no equivalent for object properties, type constraints, or other type positions.

Union types let you represent a value which is one of multiple types. You can specify the types using the new | operator.

function process(n: string|number) { /* ... */ }
process('foo'); // OK
process(42); // OK
process($('div')); // Error

In the function body of process, you can examine the type of an expression using typeof or instanceof operator. TypeScript understands these conditions and change type inference accordingly.

function process(n: string|number) {
  if (typeof n === 'number') {
    // Error because n is a number.
    console.log(n.toLowerCase());
  }
}

So far, Union types look a lot like a sum type in algebraic data type. Type A|B represents a type either A or B and you use type guards to examine the type.

However, Union types of TypeScript have more interesting properties combined with structural typing. For example, the type A|B has a property P of type X|Y if A has a property P of type X and B has a property P of type Y. Here an example taken from Spec Preview: Union types.

interface Car {
  weight: number;
  gears: number;
  type: string;
}
interface Bicycle {
  weight: number;
  gears: boolean;
  size: string;
}
var transport: Car|Bicycle = /* ... */;
var w: number = transport.weight; // OK
var g = transport.gears; // OK, g is of type number|boolean

console.log(transport.type); // Error, transport does not have 'type' property
console.log((<Car>transport).type); // OK

Because both Car and Bicycle types have weight property of number type, you can access weight property of Car|Bicycle type without using a type guard. gears property is more interesting. The type of gears is number in Car and boolean in Bicycle, so the type of gears in Car|Bicycle becomes number|boolean.

Union types also change the algorithm to find the best common type. For example, now TypeScript can infer the type of a mixed array:

var xs = [1,2,3, "hello", "world"];

In old version of TypeScript, the type of xs is {}[] because number and string have nothing in common. But with Union types, the type of xs is inferred as string|number.

In conclusion, Union types provide a succinct way to represent a value with multiple types. They help you keep “DRY principle” by removing all redundant declarations of functions, properties and type constraints.

One thought on “TypeScript Type System: Union Types

  1. Pingback: TypeScript: More on Union Types | Kwang Yul Seo

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s