Runtime type checking with io-ts in Typescript

Otto Kivikärki
3 min readNov 11, 2018

--

Typescript can statistically analyze your code, by compiling it to Javascript and throwing errors while doing it, if it sees something wrong with the way that the types are defined. However, being able to check types at runtime would be great and advantageous as well, since untyped data can come in from various different sources and cause unwanted effects on your code. The Typescript compiler cannot know what kind of data is coming from a REST API endpoint or a JSON file for example, it can only define the structures that are expected at compile-time.

This is where Giulio Canti’s io-ts library comes in. With it, you can define runtime types for the expected data, that can be validated while your application is running. For example, lets say you have a REST API that returns an array of Product objects. You want to be certain, that the data returned from the endpoint is correct, so you create a io-ts runtime type interface as such:

// types.ts 
import * as t from "io-ts";
export const Product = t.interface({
id: t.number,
name: t.string,
quantity: t.number,
type: t.union([t.literal("FURNITURE"), t.literal("BOOK")])
});
export const Products = t.array(Product);

This type can be used to validate that the attributes match the expected ones at runtime, that the id attribute is a number, name is a string, quantity is a number and type is either a string “FURNITURE” or a string “BOOK” (More available types can be found in the io-ts repository). After that I am creating an array type that can be used to check that an input array contains only valid product objects. Next, lets see how we can use these to validate an example api response:

import { Products } from './types';
import { ThrowReporter } from "io-ts/lib/ThrowReporter";
...function getProducts() {
// A mock REST API response
const apiResponse = [{
id: 1,
name: "Table",
type: "FURNITURE",
quantity: 5
}, {
id: "2",
name: "The Lord of the Rings",
type: "BOOK",
quantity: 10
}];
// Decode i.e. validate the api response
const result = Products.decode(apiResponse);
// Use a reporter to throw an error if validation fails
ThrowReporter.report(result);
// Get the validated value and use it in your application
return result.value;
}

Can you guess what will the output be after running the function? If you guessed it will throw an error (because id of the second objects is incorrectly a string), you are correct.

Invalid value "2" supplied to : Array<{ id: number, name: string, type: ("FURNITURE" | "BOOK"), quantity: number }>/1: { id: number, name: string, type: ("FURNITURE" | "BOOK"), quantity: number }/id: number

The decode method can be used to validate the input, but it doesn’t throw any validation errors itself as it returns a Validation<A> type which in turn is an Either<Errors, A> type (a type that represents a value of two possible types, more of it here if interested). The library suggests that the actual error handling should be done by a reporter. The io-ts library exports two default reporters of which the ThrowReporter can be used to actually throw an error as above. You are also able to create custom reporters by implementing the Reporter interface as sometimes you might not want to throw on error and instead report the error to a logging service for example.

If the incorrect id with the string “2” is replaced with a number 2, the validation will pass as the reporter doesn’t throw and the result variable will contain

right([{
"id": 1,
"name": "Table",
"type": "FURNITURE",
"quantity": 5
}, {
"id": 2,
"name": "The Lord of the Rings",
"type": "BOOK",
"quantity": 10
}])

from which you can unwrap the array by getting the value attribute (result.value) as on the last line in the getProducts function above.

There is a lot more to the io-ts library and its friend fp-ts. There is even a package for runtime type checking React props (prop-types-ts) which is pretty cool. This blog post has only scratched the surface and there are probably many more great things that you can achieve with the aforementioned libraries :)

--

--

Otto Kivikärki

Software developer working at Vincit Interested in modern javascript tecnologies and frameworks.