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.
Pingback: TypeScript: More on Union Types | Kwang Yul Seo