Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicit Error Types #642

Draft
wants to merge 13 commits into
base: poc
Choose a base branch
from
Draft
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"prefer-rest-params": "off",
"prefer-spread": "off"
"prefer-spread": "off",
"no-useless-escape": "off"
}
}
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"typescript.tsdk": "./node_modules/typescript/lib"
"typescript.tsdk": "./node_modules/typescript/lib",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
}
57 changes: 42 additions & 15 deletions Codec.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,66 @@
# Codec interface

```ts
export interface Codec<I, O, A> extends D.Decoder<I, A>, E.Encoder<O, A> {}
export interface Codec<D, E> {
readonly decoder: D
readonly encoder: E
}
```

A codec is just a decoder and an encoder packed together.

The following laws must hold

1. `pipe(codec.decode(u), E.fold(() => u, codec.encode) = u` for all `u` in `unknown`
2. `codec.decode(codec.encode(a)) = E.right(a)` for all `a` in `A`
A `Codec` is just a `Decoder` and its dual packed together

You can build a new codec using the `make` helper
You can build a new codec using the `codec` helper

**Example**

```ts
import * as C from 'io-ts/Codec'
import * as D from 'io-ts/Decoder'
import * as E from 'io-ts/Encoder'
import { pipe } from 'fp-ts/function'

const decoder: D.Decoder<unknown, number> = pipe(
export interface NumberFromStringE {
readonly _tag: 'NumberFromStringE'
readonly actual: unknown
}
export interface NumberFromStringLE extends LeafE<NumberFromStringE> {}

export const decoder: D.Decoder<
unknown,
D.ParseError<DE.StringLE, NumberFromStringLE>,
number
> = pipe(
D.string,
D.parse((s) => {
const n = parseFloat(s)
return isNaN(n)
? D.failure(s, `cannot decode ${JSON.stringify(s)}, should be parsable into a number`)
return isNaN(n)
? D.failure(
DE.leafE({
_tag: "NumberFromStringE" as const,
actual: n
}) as NumberFromStringLE
)
: D.success(n)
})
)

const encoder: E.Encoder<string, unknown> = {
encode: String
const encoder: D.Decoder<
number,
never,
string
> = {
encode: flow(String, D.success)
}

export const NumberFromString: C.Codec<unknown, string, number> = C.make(decoder, encoder)
export const NumberFromString: C.Codec<
D.Decoder<
unknown,
D.ParseError<DE.StringLE, NumberFromStringLE>,
number
>,
D.Decoder<
number,
never,
string
>
> = C.codec(decoder, encoder)
```