Typing, converting and validating unknown input with io-ts
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
…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
- I don’t really get the whole Either stuff and think it might be to bloated but thats to early to tell.
- I would really like to also add documentation to the shape and have that extracted automatically.
- I need to somehow walk the shape to map it to Environment variables
-
maybe this already is the problem and I should stop here… ↩
-
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 😉)
- Fix typos or improve my phrasing 💖
- Share the article to people that might also like it
- Follow me on Twitter
- Sponsor me on GitHub
- Spread love, peace and sanity
- Act to save our planet
- Be inclusive to everyone but the intolerant (because the paradox of tolerance is a thing).