Categories
Flow JavaScript

JavaScript Type Checking with Flow — Type Aliases

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 create type aliases with Flow to reuse types.

Defining Type Aliases

We can define a type alias in Flow by using the type keyword. For example, we can write:

type Person = {  
  name: string,  
  age: number  
};

Then we can use the same type alias anywhere in our code, including variable declarations:

let person: Person = {  
  name: 'Joe',  
  age: 10  
}

and function signatures:

const getPerson = (person: Person): Person => person;

and classes:

class Employee {  
  person: Person;  
  constructor(person: Person){  
    this.person = person;  
  } getPerson(person: Person): Person {  
    return this.person;  
  }  
}

Type Alias Syntax

We can assign any types to a type alias with the = operator.

For example, we can write:

type UnionAlias = 'a' | 'b' | 'c';

or:

type NumberAlias = number;

We can also define an alias for object types as follows:

type PersonAlias = {  
  name: string,  
  getName(): string,  
};

Type Alias Generics

They can also be parameterized with generic type markers. In this case, we have to pass in actual types to use it as a type for any entity.

For example, we can write:

type GenericPerson<A, B> = {  
  name: A,  
  age: B  
};

Then to use it, we have to pass in types for A and B . For example, we write:

let person: GenericPerson<string, number> = {  
  name: 'Joe',  
  age: 10  
}

Generic type markers can also be applied to methods in type aliases. For example, we can write:

type GenericObject<A, B, C> = {  
  prop: A,  
  method(val: B): C,  
};

Opaque Type Aliases

Opaque type alias hides the type in which the type alias is created from when it’s exported to different files.

We can define an opaque type alias by using the opaque keyword before the type alias definition. For example, we can write:

opaque type Name = string;

Within the file that the opaque type alias is defined, it acts like a regular type alias. For example, we can use the Name alias as follows:

function getName(name: Name): Name {  
  return name;  
}

We can also export it with the export keyword as follows:

export type {Name};

Also, we can add an optional subtyping constraint to an opaque type alias by adding a colon between the subtype and the supertype. The format would be like:

opaque type Alias: SuperType = Type;

For example, we can write:

opaque type PersonAlias = {  
  name: string;  
  age: number;  
  getAgeByName(name: string): number;  
}opaque type AliasAlias: PersonAlias = PersonAlias;  
opaque type VeryOpaque: AliasAlias = PersonAlias;

Then when AliasAlias and VeryOpaque type alias are exported, the other files won’t see the underlying types that composed the PersonAlias type alias.

Opaque Type Alias Type Checking

Within the file where the opaque type alias is defined, it behaves exactly as regular type aliases do.

For example, we can write:

opaque type StringAlias = string;('abc': StringAlias);function concat(x: StringAlias, y: StringAlias): StringAlias {  
  return x + y;  
}  
function toNumberAlias(x: string): StringAlias { return x; }  
function getLength(x: StringAlias): number { return x.length; }

Flow recognizes that StringAlias is just an alias for the string type in the file that StringAlias is defined in.

However, outside the file, StringAlias is recognized as its own type. If we import it in another file as follows:

import type { StringAlias } from './index';

and then try to use the same code as the ones we have above:

function concat(x: StringAlias, y: StringAlias): StringAlias {  
  return x + y;  
}  
function toNumberAlias(x: string): StringAlias { return x; }  
function getLength(x: StringAlias): number { return x.length; }

We’ll get errors since StringAlias is no longer to be compatible with string .

To fix this, we have to add a subtyping constraint to indicate that StringAlias is a subtype of string so that they can be considered to be compatible outside the defining file.

For example, if we export the StringAlias type in one module as follows:

opaque type StringAlias: string = string;  
export type {StringAlias};

Then we can use it in another module as follows:

import type { StringAlias } from './index';function concat(x: StringAlias, y: StringAlias): string {  
  return x + y;  
}  
function toNumberAlias(x: string): string { return x; }  
function getLength(x: StringAlias): number { return x.length; }

As we can see, unlike what we had before, now StringAlias is considered to be compatible with string outside the file that StringAlias is defined in. This is because we made StringAlias a subtype of string with:

opaque type StringAlias: string = string;

Opaque Type Alias Generics

We can add generic type markers to opaque type alias like regular type alias. For example, we can write:

opaque type Obj<A, B, C>: { a: A, b: B } = {  
  a: A,  
  b: B,  
  c: C,  
};

After defining the generic type alias, we can use it by adding actual types in place of the generic type markers. For instance, we can write:

let obj: Obj<number, string, boolean> = {  
  a: 1, b: 'Joe', c: false  
}

Library Definitions

We can use the declare keyword to declare opaque type alias in library definitions. This way, we can omit the underlying type and still optionally include a supertype.

For instance, we can write:

declare opaque type Obj;  
declare opaque type NumberAlias: number;

With Flow, we can define regular and opaque type alias. Regular type alias lets us set any type alias to a name. Opaque type alias hides the underlying type definition in the file that imports the type. We can set a type alias as a supertype of an opaque type alias to make the opaque type alias be compatible with the supertype outside the file that the opaque type is defined in.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *