Categories
Flow JavaScript

JavaScript Type Checking with Flow — Union Types

Spread the love

Flow is a type checker made by Facebook for checking JavaScript data types. It has many built-in data types we can use to annotate the types of variables and function parameters.

In this article, we’ll look at how to use union types to create variables that accept several different types.

Union Type Syntax

The general syntax for defining a union type is as follows:

Type1 | Type2 | ... | TypeN

We separate each type with the vertical bar | .

Also, we can break them into multiple lines by using leading | :

type Bar =  
  | Type1  
  | Type2  
  | ...  
  | TypeN

We can create union types out of other union types:

type Numbers = 1 | 2;  
type Fruit = 'apple' | 'orange';type Foo = Numbers | Fruit;

Functions

If we use union types in functions, we have to handle every possible type of the union type of the parameter. For example, we have to write something like:

function foo(value: number | boolean | string):  number {   
  if (typeof value === 'number') {  
    return 1;  
  } else if (typeof value === 'boolean') {  
    return 2;  
  }  
  return Number(value);  
}

If we skip the handling of any type in the union type of the parameter like the following code:

function foo(value: number | boolean | string):  number {   
  if (typeof value === 'number') {  
    return 1;  
  } else if (typeof value === 'boolean') {  
    return 2;  
  }    
}

then we’ll get an error.

Unions & Refinements

With union type parameters, it’s useful to handle each type individually. We can do this with the typeof operator to check for the type and handle each type as follows:

function foo(value: number | boolean | string): number {   
  if (typeof value === 'number') {  
    return 1;  
  }   
  else if (typeof value === 'boolean') {  
    return 2;  
  }   
  else if (typeof value === 'string') {  
    return Number(value);  
  }  
  return Number(value);  
}

Disjoint Unions

We can create a union type out of different object types. For example, if we have the following objects:

{ foo: true, bar: 'bar' }  
{ foo: false, bar: 'baz' }

We can create a union type out of these 2 objects by creating a type alias for each and then union them. So we can write:

type Foo1 = { foo: true, bar: 'bar' };  
type Foo2 = { foo: false, bar: 'baz' };
type Foo = Foo1 | Foo2;

Then we can create variables of type Foo as follows:

let foo1: Foo = { foo: true, bar: 'bar' };  
let foo2: Foo = { foo: false, bar: 'baz' };

The union of 2 object types with fixed values will allow our variables to take on either of the 2 types in the union.

Union of object types with properties that aren’t exact will create a union of the 2 types for the properties.

For example, if we have:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { foo: true, bar: number };  
type Foo = Foo1 | Foo2;

Then Foo will allow bar to be either a string or a number, while foo will always be true . So we can create variables like the following:

let foo1: Foo = { foo: true, bar: 'bar' };  
let foo2: Foo = { foo: true, bar: 2 };

If we have 2 types with different properties, then the union of 2 types let us include all the properties of the object if we have a variable with the union of the 2 types.

For example, if we have:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { baz: false, a: number };
type Foo = Foo1 | Foo2;

Then we can define a variable with all the properties as follows:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1 };

With union types, we can also pass in extra properties that aren’t included in the types that form the union. So given:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { baz: false, a: number };
type Foo = Foo1 | Foo2;

So we can write:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1, b: 2 };

Disjoint Union with Exact Types

We can restrict the addition of new properties by creating a union out of exact types as follows:

type Foo1 = {| foo: true, bar: string |};  
type Foo2 = {| baz: false, a: number |};
type Foo = Foo1 | Foo2;

Then writing:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1, b: 2 };

will give us an error.

With unions of exact types, we can either have properties from one or the other. So given these types:

type Foo1 = {| foo: true, bar: string |};  
type Foo2 = {| baz: false, a: number |};
type Foo = Foo1 | Foo2;

We can either have:

let foo: Foo = { foo: true, bar: 'bar' };

or:

let foo: Foo = {  baz: false, a: 1 };

With union types, we can create variables that can take on the value of several types. The types that form a union can primitive or object types.

We can also form unions of exact types to prevent adding additional properties that aren’t listed in the types or including properties from all the types.

Leave a Reply

Your email address will not be published.

If you like the content of this blog, subscribe to my email list to get exclusive articles not available to anyone else.