gcanti / io-ts-types

A collection of codecs and combinators for use with io-ts
https://gcanti.github.io/io-ts-types/
MIT License
311 stars 40 forks source link

Suggestion: use refined input type instead of `unknown` #135

Open OliverJAsh opened 4 years ago

OliverJAsh commented 4 years ago

šŸ› Bug report

Current Behavior

Types like NumberFromString currently allow decoding from any type (unknown). This can be useful but also error prone. For example, we might accidentally pass in a value which is already a number. We won't get a type error because decode is asking for unknown, which number is assignable to.

NumberFromString.decode(100) // no type error, returns a Left

Expected behavior

Reproducible example

Suggested solution(s)

If we changed the input from unknown to string, then decode would only work with strings. This way, mistakes like the one above would result in helpful type errors at compile time:

NumberFromString.decode(100) // type error!

NumberFromString.decode('100') // no type error, returns a Right

Implementation:

export interface NumberFromStringC extends t.Type<number, string, string> {}
export const NumberFromString: NumberFromStringC = new t.Type<number, string, string>(
  'NumberFromString',
  t.number.is,
  (u, c) => {
    const n = +u;
    return isNaN(n) || u.trim() === '' ? t.failure(u, c) : t.success(n);
  },
  String,
);

This is how I would expect the type to behave based on its name ("from string").

If the user needs to decode a value of type unknown, they can use t.string.pipe(NumberFromString) instead of NumberFromString.

WDYT? If you agree I will happily send a PR!

Additional context

Your environment

Which versions of io-ts-types are affected by this issue? Did this work in previous versions of io-ts-types?

Software Version(s)
fp-ts
io-ts
io-ts-types
TypeScript
OliverJAsh commented 4 years ago

@gcanti Do you think the same change should be made to NonEmptyString (i.e. accept a string as input), or do you think that should remain as unknown?