Categories
Flow JavaScript

JavaScript Type Checking with Flow — More Data Types

Spread the love

Flow is a type checker made by Facebook for enforcing data types in JavaScript. 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 some data types unique to Flow, including maybe types, literal types, mixed and any types

Maybe Types

Maybe types are for annotating variables or parameters that are optional. It’s indicated by a question mark in front of the type name.

For example, ?string and ?number are maybe types for string and number respectively.

With ?string type, we can set it with a string, null, undefined, or nothing. For example, we can write:

let a: ?string = null;  
let b: ?string = undefined;  
let c: ?string;  
let d: ?string = 'abc';

The rules are the same for parameters:

maybeString(null);  
maybeString(undefined);  
maybeString();  
maybeString('abc');

Anything else, like:

maybeString(0);

will get us errors.

Optional Object Properties

We can mark an object property as optional by adding a ? after the property name.

For example, we can write:

let x: { foo?: string } = {};  
let y: { foo?: string } = { foo: 'abc' };

This rule also applied to parameters, so the following code will work:

function optionalFoo(val:  { foo?: string }){}
optionalFoo({});  
optionalFoo({ foo: 'abc'});

undefined also works. For example, we can write:

let y: { foo?: string } = { foo: undefined };

We can also write:

function optionalFoo(val:  { foo?: string }){}  
optionalFoo({ foo: undefined });

Any other values for the foo property will result in an error.

Default Function Parameter Values

Like JavaScript since ES2015 or later, we can assign default values to function parameters. For example, we can write:

function foo(value: string = "foo") {}

The default value for the value parameter is 'foo' .

We can then pass in a string, undefined or nothing to the foo function. For example, the following will work:

foo("bar");       
foo(undefined);
foo();

Passing in undefined or nothing will result in 'foo' being the value for the value parameter.

Everything else passed in will result in an error. For example, the following won’t work:

foo(null);

Literal Types

Flow has literal types to let us create variables or function parameters that can only be assigned a few possible values.

For example, we can write:

let x: 1|2 = 1;

Since x has the type 1|2 which means that x can only be assigned the value 1 or 2. Each possible value is separated by the | symbol. The | symbol is the union type operator. It indicates the type is either of the types indicated in the operands.

If we write:

let y: 1|2 = 3;

We get an error since x has been assigned a value that’s not in the list of possible values for y .

This is the same for function parameters. For example, the following will work:

function fruitFn(fruit: 'orange' | 'apple'){}fruitFn('apple');

While the following code will fail to compile:

function fruitFn(fruit: 'orange' | 'apple'){}fruitFn('banana');

Mixed Type

The mixed type lets us assign any value to a variable or a function parameter. For example, we can write:

function foo(value: mixed) {  
    
}
foo('apple');  
foo(1);  
foo(true);

With mixed types, we have to figure out the actual type before doing manipulating the parameter or variable. We can use the typeof or instanceof operator for checking the type before operating on them.

For example, we can check if a value is a string or number as follows:

function foo(value: mixed) {  
  if (typeof value === 'number'){  
    return value;  
  }  
  else if (typeof value === 'string'){  
    return +value;  
  }  
}

We can also use the instanceof operator to check the constructor the object is created from and run code accordingly:

function foo(value: mixed) {  
  if (value instanceof Date){  
    return value.toUTCString();  
  }  
  else if (value instanceof Object){  
    return value.toString();  
  }  
}

If Flow doesn’t know what type of object we’re passing in, then it won’t accept the code. For example, we can’t write:

function foo(value: mixed) {  
  return value.toString();  
}

We’ll get the error:

[Flow] Cannot call `value.toString` because property `toString` is missing in mixed [1].

Any Type

The any type let us opt out of type checking with Flow. It lets us assign it to anything if it’s a variable and pass in anything if it’s a function parameter.

It doesn’t check if the method we’re calling actually exists or the operator we’re using actually works with the data we’re passing in.

any type isn’t the same as the mixed type. mixed type is checked for the type. We’ve to check the type with our code because we can do anything with the variable or parameter with the mixed type.

Variables or parameters with the any type do not have any type check, so Flow lets us do anything we want with variables of any type. This means that we can get run-time errors like any other JavaScript code if we didn’t check the type of data is being manipulated.

When variables or parameters of type any are assigned to other variables without type annotation. It’ll be set as the any type.

We should stop this by annotating our variables or parameters with a type.

For example, we should write:

function fn(obj: any) {  
  let bar: number = obj.bar;  
}

Then type checking will be done on bar . This means that

function fn(obj: any) {  
  let bar: number = obj.bar + 1;    
}

will work, but

function fn(obj: any) {  
  let bar: number = obj.bar + 'abc';    
}

will fail.

Flow has many data types that make type annotation convenient coming from JavaScript. We can use the mixed type to pass in anything or assign a variable to anything, but then we can check the type in our code after passing in the value or variable assignment.

any type bypasses Flow’s type checking and lets us assign any value or pass any value to a function.

Literal types lets us set a variable or parameter to the possible values listed, separated by a | symbol.

Maybe types make properties or variables be optionally passed in or stay undefined .

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 *