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

Factory methods and other helpers #172

Open
spacejack opened this issue May 15, 2018 · 3 comments
Open

Factory methods and other helpers #172

spacejack opened this issue May 15, 2018 · 3 comments
Milestone

Comments

@spacejack
Copy link

Hi, I've been using io-ts a bit lately (thanks again for the great library) and summarized some ideas I've had in this repo.

Brief summary:

  • Using nominal (branded) types with io-tos and merging those identifiers.
  • I don't always want to deal with Either; sometimes I just want it to throw an exception on decoding.
  • decode returning a reference to the same object was a bit surprising, especially if the input has extra properties.

My general solution was to subclass t.Type and patch t.InterfaceType/IntersectionType/PartialType to add an of method.

@gcanti
Copy link
Owner

gcanti commented May 15, 2018

Hi @spacejack, thanks for sharing your ideas.

Some other options I can think of:

Using nominal (branded) types with io-tos and merging those identifiers

You can brand any runtime type by changing its A type parameter

export function brand<RT extends t.Any, A, O, I>(
  type: t.RefinementType<RT, A, O, I>
): <B>() => t.RefinementType<RT, A & B, O, I>
export function brand<A, O, I>(type: t.Type<A, O, I>): <B>() => t.Type<A & B, O, I>
export function brand<A, O, I>(type: t.Type<A, O, I>): <B>() => t.Type<A & B, O, I> {
  return () => type as any
}

// const Positive: t.RefinementType<t.NumberType, number & Record<"__Positive", never>, number, t.mixed>
export const Positive = brand(t.refinement(t.number, n => n >= 0, 'Positive'))<Record<'__Positive', never>>()

// const UserId: t.Type<string & Record<"__UserId", never>, string, t.mixed>
export const UserId = brand(t.string)<Record<'__UserId', never>>()

I don't always want to deal with Either; sometimes I just want it to throw an exception on decoding

You could patch the t.Type class

declare module 'io-ts' {
  interface Type<A, O, I> {
    /** unsafe version of `decode` */
    of: (i: I) => A
  }
}

t.Type.prototype.of = function(i) {
  return this.decode(i).getOrElseL(() => {
    throw new Error('Invalid ' + this.name)
  })
}

console.log(Positive.of(1)) // 1
console.log(Positive.of(-1)) // throws

decode returning a reference to the same object was a bit surprising, especially if the input has extra properties

decode, if possible, should not strip extra fields because it would

  • add a performance hit
  • break t.intersection

Relevant #147

@spacejack
Copy link
Author

Ok, thanks for the feedback and for the branding examples!

@orzFly
Copy link

orzFly commented May 30, 2018

About helpers, I have a strange and opinionated library named Loi: https://github.com/orzFly/node-loi

const SomeType = Loi.array(Loi.object({ a: Loi.number().positive() }, { b: Loi.string }))
type SomeType = {a: number, b?: string}[]

@gcanti gcanti added this to the 2.0 milestone Jan 6, 2019
@gcanti gcanti modified the milestones: 2.0, 3.0 Jul 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants