TL;DR

This is not about the internet of things.

I’m currently evaluating io-ts to handle unknown input (such as user configurations) in typescript projects.

Context

I’m working on a command line utility for creating pdf files from user input.

Problem

When it comes to configuration I want to support a lot of different inputs for configuration.1

I want to support:

  • cosmiconfig
  • Environment Variables (FOO=bar)
  • Command Line Arguments (--foo=bar)

But none of these produce anything reliable or event typed2 😱!

Idea

The main idea is to declare the shape of the (to be expected) configuration in a way that not only produces a Typescript Type but also validates the input at runtime.

function app(input: unknown) {
  const configShape = {
    count: Number,
    deep: {
      stuff: String
    },
  };
  const config = validate(input, configShape);

  /* this should now work without type warnings */
  const num: number = config.count;
  const str: string = config.deep.stuff;
}

type Magic<S> = /* Recursively map NumberConstructor to number etc. */

function validate<S>(input: unknown, shape: S): Magic<S> {
  /* 
  throw Error when input is invalid
  maybe convert it and return it as correct type
  */
}

I already went down this road and even implemented optional types and arrays of single types but before I started working on unions and intersections I stopped and searched npm again.

Solution

npm inatall io-ts

…well I guess, I wrote this article before finishing the migration.

I wont get into details here but instead refer to the well written documentation

import * as t from 'io-ts';
import { isLeft } from 'fp-ts/lib/Either'
import { ThrowReporter } from 'io-ts/lib/ThrowReporter'

function app(input: unknown) {
  const configShape = t.type({
    count: t.number,
    deep: t.type({
      stuff: t.string
    })
  });

  const c = configShape.decode(input);
  if (isLeft(c)) {
    throw ThrowReporter.report(c);
  }

  /* this should now work without type warnings */
  const num: number = c.right.count;
  const str: string = c.right.deep.stuff;
}

Next

  1. I don’t really get the whole Either stuff and think it might be to bloated but thats to early to tell.
  2. I would really like to also add documentation to the shape and have that extracted automatically.
  3. I need to somehow walk the shape to map it to Environment variables

  1. maybe this already is the problem and I should stop here… 

  2. with the exception of ts config files thanks to cosmiconfig-typescript-loader [which is slooow, but thats another story] 

 


 

Was this post valuable for you?

Cool! Here is how you can give back if you want to: (only pick a few 😉)

Thanks for reading! Be safe ✌️