Rick Carlino

Lead software developer and a co-founding member at FarmBot

Co-founder and Maker of Things at Fox.Build Makerspace in St. Charles, IL.

[ 📨 Newsletter ] [ 💬 Contact ] [ 👤 About ]


Interesting Finds: io-ts Runtime Type Checker

Although there are many benefits to using Typescript, most folks ultimately choose it to achieve type safety without sacrificing Javascript interoperability.

Some situations need extra caution, though. For instance, there is no way for a compiler to type check the types of I/O operations. The Typescript compiler has no control over the outside world- this includes things like AJAX, Websockets and file operations.

One way to bring safety to a project’s I/O operations is to write user defined type guards.

Type guard screenshot

It’s a great way to handle unknown runtime types, but it can create excess boilerplate and conditional logic as well.

One library I recently discovered to tackle the issue of runtime type checking is io-ts. Although I haven’t used it in any major projects yet, it does look like a promising way to add type safety to unsafe runtime operations. It uses type guards under the hood, but comes with many conveniences to keep code DRY.

The documentation does a good job of explaining how it works.

Here’s an example that downloads JSON data over AJAX (via the Axios AJAX library):

import * as t from "io-ts";
import axios from "axios";

// This can be used as a runtime validator:
export const RuntimeCustomer = t.interface({
  name: t.string,
  orgId: t.union([t.number, t.undefined])
});

// A compile time equivalent can be attained
// using t.TypeOf<MY_TYPE_HERE>():
type CompileTimeCustomer = t.TypeOf<typeof RuntimeCustomer>;

// This URL is properly formed:
const GOOD_JSON_URL = "//api.myjson.com/bins/yr0wx";

// This URL is not:
const BAD_JSON_URL = "//api.myjson.com/bins/nfsf5";

// Download some uncertain data and type check it at runtime!
axios
  .get(GOOD_JSON_URL) // BAD_JSON_URL triggers validation failure.
  .then(resp => {
    let possiblyCustomer = resp.data;
    // RuntimeCustomer.is() is a "user defined type guard".
    if (RuntimeCustomer.is(possiblyCustomer)) {
      return possiblyCustomer;
    } else {
      throw new Error("Validation failed!");
    }
  });

Looking at it in an editor, we can see that Typescript has no problem inferring the type:

Io ts screenshot

Io-ts looks like a promising, actively maintained project. I hope more developers give it a look.

The example above is only scratching the surface. If you are curious to learn more, check them out on NPM or Github.