Since everything newtypes-ts does is also possible with branded types in io-ts, I'd only use io-ts. The only downside I see is that newtypes-ts exposes convenient types (such as Integer, Char or NonEmptyString) that must be reimplemented using t.Branded.
(there's actually a NonEmptyString codec and a fromNewtype function in the io-ts-types package, but I couldn't manage to make this function work. I believe it uses the newer io-ts APIs, which are still marked as experimental as of today. Feel free to give it a try though :) )
On a side note, I'd use t.brand instead of defining a new t.Type, as it requires less boilerplate.
If you are working with newtypes, you'll have to convert them into Branded types. Indeed, both of these types are not defined in the same way, so they don't interoperate very well. To manage that, we could use some "adapter" to transform a Newtype into a Branded:
// Convert a Newtype<A, B> into a Branded<B, A>typeToBranded<A>=AextendsNewtype<inferBrand,inferUnderlyingType>?t.Branded<UnderlyingType,Brand>:never// extract the brand B from a Branded<A, B>, used to define codecs type guardstypeGetBrand<A>=Aextendst.Brand<inferBrand>?Brand:never
Now we can do the following:
import*asNESfrom'newtype-ts/lib/NonEmptyString'typeNonEmptyString=ToBranded<NES.NonEmptyString>constNonEmptyString=t.brand(t.string,(s:string):sist.Branded<string,GetBrand<NonEmptyString>>=>s.length>0,'NonEmptyString')typeString60=ToBranded<Newtype<{readonlyString60:uniquesymbol},string>>constString60=t.brand(t.string,(s:string):sist.Branded<string,GetBrand<String60>>=>s.length<=60,'String60')constNonEmptyString60=t.intersection([NonEmptyString,String60])typeNonEmptyString60=t.TypeOf<typeofNonEmptyString60>// or: type NonEmptyString60 = NonEmptyString & String60
Note: we could also directly define Branded types and completely discard Newtype:
Hello, thank you for your feedback!
If you want to avoid using a type assertion there, you can create a predicate function:
And use it both for the type guard and validate functions of your codec:
Since everything newtypes-ts does is also possible with branded types in io-ts, I'd only use io-ts. The only downside I see is that newtypes-ts exposes convenient types (such as
Integer
,Char
orNonEmptyString
) that must be reimplemented usingt.Branded
.(there's actually a
NonEmptyString
codec and afromNewtype
function in the io-ts-types package, but I couldn't manage to make this function work. I believe it uses the newer io-ts APIs, which are still marked as experimental as of today. Feel free to give it a try though :) )On a side note, I'd use
t.brand
instead of defining anew t.Type
, as it requires less boilerplate.If you are working with newtypes, you'll have to convert them into
Branded
types. Indeed, both of these types are not defined in the same way, so they don't interoperate very well. To manage that, we could use some "adapter" to transform aNewtype
into aBranded
:Now we can do the following:
Note: we could also directly define
Branded
types and completely discardNewtype
:Hope that helps :)