transformer-io-ts
v1.1.9
Published
transforms types to io-ts entities
Downloads
3
Maintainers
Readme
io-ts-transformer
TypeScript transformer which converts TypeScript types to io-ts entities Clone of the Original Repository with the working update
Requirement
TypeScript >= 3.5.2
io-ts >= 2.x+
fp-ts >= 2.0.0
Installation
To install this package run the following command:
npm i io-ts-transformer io-ts fp-ts
About this project
Io-ts is an awesome library that makes possible runtime type validation in TypeScript. It has many advantages, but there is one fundamental flaw: it makes us duplicate information in our projects, which leads to many other problems. Let's look at this simple structure:
type User = {
name: string
age: number
}
As information about this type will be lost when compiling from TypeScript to JavaScript, we need to describe an io-ts entity that would be able to validate our data at runtime.
import * as t from 'io-ts'
const user = t.type({
name: t.string,
age: t.number
})
This approach has significant disadvantages:
Duplicating the same information in different forms takes time for the developer.
If you need to change TypeScript data type, you must not forget to change the io-ts model too. Otherwise, you can get an elusive bug or vulnerability in the system security.
The solution is automatic compile-time transformation of TypeScript types into io-ts models. Io-ts-transformer does this for you.
Package structure
This package exports 2 functions.
One is buildDecoder
which is used in TypeScript code to convert TypeScript types into io-ts entities, while the other is a TypeScript custom transformer which is used to compile the buildDecoder
function correctly.
Io-ts-transformer already can:
- Transform almost all TypeScript types into io-ts models:
- null, undefined, void, unknown
- string, boolean and number literals
- string, boolean and number types
- arrays, tuples, records, objects and functions
- type unions and intersections
- interfaces and recursive types
- Compute expressions passed into it.
For example, this expression
buildDecoder<Omit<{ foo: 'bar', bar: number } & { data: string }, 'bar'>>()
will be converted into
import * as t from 'io-ts'
t.type({ foo: t.literal('bar'), data: t.string })
- Inject existing io-ts models into a TypeScript type. You can use
FromIoTs
type provided by io-ts-transformer to specify variables to inject. Then io-ts models, built by io-ts-transformer, will reference the variables passed.
import { withFallback } from 'io-ts-types/lib/withFallback'
import { buildDecoder, FromIoTs } from 'io-ts-transformer'
import * as t from 'io-ts'
const options = t.type({
logLevel: withFallback(t.string, 'info')
})
type Config = {
folder: string
options: FromIoTs<typeof options>
}
// const config = t.type({ folder: t.string, options: options })
const config = buildDecoder<Config>()
Don't forget wrapping your models in t.recursion
, if you use buildDecoder
inside them, and the type passed includes FromIoTs
recursive reference to this model. For example, this code will produce a runtime error:
import { buildDecoder, FromIoTs } from 'io-ts-transformer'
import * as t from 'io-ts'
const foo = t.type({ bar: buildDecoder<T>() })
type T = { foo?: FromIoTs<typeof foo> }
// Will produce a runtime error
const decodeResult = foo.decode({ bar: {} })
To avoid this you should wrap foo
model in t.recursion
:
import { buildDecoder, FromIoTs } from 'io-ts-transformer'
import * as t from 'io-ts'
const foo = t.recursion('foo', () => t.type({ bar: buildDecoder<T>() }))
type T = { foo?: FromIoTs<typeof foo> }
// Now it will work as intended
const decodeResult = foo.decode({ bar: {} })
Io-ts-transformer can't do
Transform classes.
Work with dynamic type parameters, i.e.
buildDecoder<T>()
in the following code will be converted intot.void
as default io-ts entity:
import { buildDecoder } from "io-ts-transformer";
function convertEntity<T>(entity: T) {
return buildDecoder<T>();
}
How to use buildDecoder
import { buildDecoder } from "io-ts-transformer";
type User = {
name: string;
age: number;
};
// const usersDecoder = t.array(t.type({ name: t.string, age: t.number }))
const usersDecoder = buildDecoder<Array<User>>();
How to use the custom transformer
Unfortunately, TypeScript itself does not currently provide any easy way to use custom transformers (See https://github.com/Microsoft/TypeScript/issues/14419). The followings are the example usage of the custom transformer.
webpack (with ts-loader or awesome-typescript-loader)
// webpack.config.js
const ioTsTransformer = require("io-ts-transformer/transformer").default;
module.exports = {
// ...
module: {
rules: [
{
test: /\.ts$/,
loader: "ts-loader", // or 'awesome-typescript-loader'
options: {
// make sure not to set `transpileOnly: true` here, otherwise it will not work
getCustomTransformers: (program) => ({
before: [ioTsTransformer(program)],
}),
},
},
],
},
};
Rollup (with rollup-plugin-typescript2)
// rollup.config.js
import resolve from "rollup-plugin-node-resolve";
import typescript from "rollup-plugin-typescript2";
import ioTsTransformer from "io-ts-transformer/transformer";
export default {
// ...
plugins: [
resolve(),
typescript({
transformers: [
(service) => ({
before: [ioTsTransformer(service.getProgram())],
after: [],
}),
],
}),
],
};
ttypescript
See ttypescript's README for how to use this with module bundlers such as webpack or Rollup.
// tsconfig.json
{
"compilerOptions": {
// ...
"plugins": [
{ "transform": "io-ts-transformer/transformer" }
]
},
// ...
}
License
MIT