Remix.run Logo
sizediterable 2 days ago

Need it be said https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

tomjakubowski 2 days ago | parent | next [-]

Libraries like runtypes, zod, et al. market themselves as validation libraries, but they function as parsing libraries in the sense this article means: with them you "parse" untyped POJOs at the I/O boundary and get typed values (or a raised exception) out the other end.

Typescript language features like branded types, private constructors can make it so those values can only be constructed through the parse method.

They're really not much different, in terms of type safety*, from something like Serde.

*: they are of course different in other important ways -- like that Serde can flexibly work with all kinds of serialized formats.

nayajunimesh 2 days ago | parent [-]

We actually use the idea of branded types in one of the validators (iso8601), and I also understand that it doesn't replace fully transformed values.

https://github.com/nimeshnayaju/valleys?tab=readme-ov-file#i...

nayajunimesh 2 days ago | parent | prev | next [-]

I understand completely, and the library is intentionally unopinionated in that regard. We simply ensure that the value passed matches the provided schema and ruleset and refine the type in-place.

In certain cases (like validating that an input is ISO8601 format), we refine the input type to a branded type (we have a Iso8601 branded type). At runtime it's just a string, but at compile time TypeScript treats it as a distinct type that can only be obtained through validation. But, it is still not transforming or parsing the data in the way that the blog post intends, which is by design.

https://github.com/nimeshnayaju/valleys?tab=readme-ov-file#i...

ale 2 days ago | parent | prev [-]

Libraries like these are meant for runtime validation. I agree though. I prefer to use the compiler itself (tsc --noEmit) than recreating the validation logic.

mirekrusin 2 days ago | parent [-]

It doesn't compete with static type system, it complements it. Static type system in typescript can't do anything with unknown/any values that are crossing i/o boundary - they require runtime assertion to bring them into statically typed, safer world.