gcanti / io-ts

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

Add tip about getting encoded types from the encode function return type #445

Open leilapearson opened 4 years ago

leilapearson commented 4 years ago

🚀 Feature request

This is just a small request to update the README with another small tip that I think people would find helpful. In addition to mentioning that you can get the static type using type User = t.TypeOf<typeof User>, people might find it helpful if the README also mentions that you can get the encoded type easily too using type EncodedUser = ReturnType<typeof User.encode>.

Current Behavior

No tip available about how to get the encoded data type from a codec.

Desired Behavior

Add a tip. See below.

Suggested Solution

The README currently mentions:

Static types can be extracted from codecs using the TypeOf operator:

type User = t.TypeOf<typeof User>

// same as
type User = {
  userId: number
  name: string
}

This probably isn't the best place to mention ReturnType though since the static type here is the same as the return type of encode due to the fact that only codecs representing primitive types were used. It really only becomes useful when using branded types...

I'm thinking where it says this:

// a unique brand for positive numbers
interface PositiveBrand {
  readonly Positive: unique symbol // use `unique symbol` here to ensure uniqueness across modules / packages
}

const Positive = t.brand(
  t.number, // a codec representing the type to be refined
  (n): n is t.Branded<number, PositiveBrand> => n >= 0, // a custom type guard using the build-in helper `Branded`
  'Positive' // the name must match the readonly field in the brand
)

type Positive = t.TypeOf<typeof Positive>
/*
same as
type Positive = number & t.Brand<PositiveBrand>
*/

Something like this could be added:

The encoded type can also be extracted from the codec's encode() function return type using TypeScript's ReturnType operator.

type EncodedPositive = ReturnType<typeof Positive.encode>

same as
type EncodedPositive = number

Who does this impact? Who is this for?

May be helpful to newer users of TypeScript and io-ts. It's not exactly hard to figure this out on your own, but it does help to show how easy it is.

Encoded / DTO types / run time types are useful in test code when specifying object literals to represent valid and invalid data. They are also useful in production code when sharing interfaces between loosely coupled components - for example when defining data structures that get passed in events.

Describe alternatives you've considered

Not doing this :-)

Additional context

ReturnType isn't exactly front and center in the TypeScript documentation. I was happy to find it when I did.

Note sure about the naming in my example. In my code I usually call these types something like PositiveDto - and since I'm using codecs that don't transform the values these types represent the runtime types of both the encoded and decoded values.

Not sure if there is an easy way to get the type of the value in the Right that will result from a successful decode - but if there is then that might be helpful to mention too.

P.S. I can submit a pull request if that would be helpful. Just let me know.

Your environment

Software Version(s)
io-ts 2.1.3
fp-ts 2.5.3
TypeScript 3.8.3
gcanti commented 4 years ago

Not sure if there is an easy way to get the type of the value in the Right that will result from a successful decode - but if there is then that might be helpful to mention too

That's TypeOf

you can get the encoded type easily too using type EncodedUser = ReturnType

There's an OutputOf operator

type EncodedPositive = t.OutputOf<typeof Positive>

/*
same as
type EncodedPositive = number
*/
leilapearson commented 4 years ago

t.OutputOf is exactly what I was looking for. Thanks @gcanti ! I didn't spot that one :-)

blakepettersson commented 4 years ago

There's an OutputOf operator

type EncodedPositive = t.OutputOf<typeof Positive>

/*
same as
type EncodedPositive = number
*/

Is there an equivalent for this in the Decoder module @gcanti? I can only see InputOf<D> and TypeOf<D> there.