Union types

Overview

Union types are a powerful way to express a value that can be one of several types. For example, you might have an API for running a program that takes a commandline as either a string, a string[] or a function that returns a string. You can now write:

  1. interface RunOptions {
  2. program: string;
  3. commandline: string[]|string|(() => string);
  4. }

Assignment to union types works very intuitively – anything you could assign to one of the union type’s members is assignable to the union:

  1. var opts: RunOptions = /* ... */;
  2. opts.commandline = '-hello world'; // OK
  3. opts.commandline = ['-hello', 'world']; // OK
  4. opts.commandline = [42]; // Error, number is not string or string[]

When reading from a union type, you can see any properties that are shared by them:

  1. if (opts.length === 0) { // OK, string and string[] both have 'length' property
  2. console.log("it's empty");
  3. }

Using Type Guards, you can easily work with a variable of a union type:

  1. function formatCommandline(c: string|string[]) {
  2. if (typeof c === 'string') {
  3. return c.trim();
  4. }
  5. else {
  6. return c.join(' ');
  7. }
  8. }

Stricter Generics

With union types able to represent a wide range of type scenarios, we’ve decided to improve the strictness of certain generic calls. Previously, code like this would (surprisingly) compile without error:

  1. function equal<T>(lhs: T, rhs: T): boolean {
  2. return lhs === rhs;
  3. }
  4. // Previously: No error
  5. // New behavior: Error, no best common type between 'string' and 'number'
  6. var e = equal(42, 'hello');

With union types, you can now specify the desired behavior at both the function declaration site and the call site:

  1. // 'choose' function where types must match
  2. function choose1<T>(a: T, b: T): T { return Math.random() > 0.5 ? a : b }
  3. var a = choose1('hello', 42); // Error
  4. var b = choose1<string|number>('hello', 42); // OK
  5. // 'choose' function where types need not match
  6. function choose2<T, U>(a: T, b: U): T|U { return Math.random() > 0.5 ? a : b }
  7. var c = choose2('bar', 'foo'); // OK, c: string
  8. var d = choose2('hello', 42); // OK, d: string|number

Better Type Inference

Union types also allow for better type inference in arrays and other places where you might have multiple kinds of values in a collection:

  1. var x = [1, 'hello']; // x: Array<string|number>
  2. x[0] = 'world'; // OK
  3. x[0] = false; // Error, boolean is not string or number