gcanti / io-ts

Runtime type system for IO decoding/encoding
https://gcanti.github.io/io-ts/
MIT License
6.68k stars 331 forks source link

Question: Convert a form to a type with different structure #710

Open Massolari opened 6 months ago

Massolari commented 6 months ago

❓ Question

My current understanding

After reading the documentation, I understood that we can use Decoder.struct to convert a Form to, I could say, a FormValidated as below:

import * as D from "io-ts/Decoder"

type Form = {
  foo: string
  bar: number
}

type FormValidated = {
  foo: NonEmptyString
  bar: 0 | 1
}

const formDecoder = D.struct({
  foo: /* Decoder to parse it */,
  bar: /* Decoder to parse it */
})

Desired Behavior

My question is if there is a way to do the same thing for a type with a different structure. It's common here in the project I work on that we have a type that the backend expects and its structure isn't the same as the form. For example, can I convert Form to MyType in this example:

type Form = {
  foo: string
  bar: number
}

type MyType = {
  foobar: NonEmptyString
  barbaz: 0 | 1
}

I see that I can use Decoder.struct to parse the form to an intermediary type (this would be the FormValidated) and then use Decoder.map to construct MyType, but is there a way to do that directly?

Suggested Solution

I implemented a function to achieve that, the way I use it is:

import * as D from "io-ts/Decoder"
import * as DH from "./decoder-helper"

type Form = {
  foo: string
  bar: number
}

type MyType = {
  foobar: NonEmptyString
  barbaz: 0 | 1
}

const formDecoder = DH.top<Form, MyType>({
  foobar: DH.lift("foo", /* Decoder to parse it */),
  barbaz: DH.lift("bar", /* Decoder to parse it */)
})

This way I can convert Form directly to MyType without needing to convert it to an intermediary type.

My function is a bit complex, that's why I'm asking if there is already a way to do that.

Who does this impact? Who is this for?

This would benefit everyone that is using this library as a form decoder or even to decode JSON that has a different structure than the target type

Describe alternatives you've considered

Decoder.struct + Decoder.map

Additional context

I was inspired by this elm package

Your environment

Software Version(s)
io-ts ^2.2.21
fp-ts ^2.12.1
TypeScript ^5.1