3 ms·
This is a fantastic point, and a huge issue that I had with TypeScript. Libraries for TypeScript like Zod[0] allow you to define types and validate against the
by shepherdjerred 3y ago
This is a fantastic point, and a huge issue that I had with TypeScript.
Libraries for TypeScript like Zod[0] allow you to define types and validate against them at runtime. For example: https://stackblitz.com/edit/typescript-pwzng4?file=index.ts https://stackblitz.com/edit/typescript-pwzng4?file=index.ts
At work, we use this to validate API responses, local storage contents, or URL/router data. It solves the problem you described -- dirty data coming from outside of the type system.
This isn't a plug for Zod or TypeScript -- there are other similar libraries for TypeScript, and I would imagine other statically typed languages have something to fill this role.
[0]: https://zod.dev/ https://zod.dev/
- svrtknst 3y agoTypeScript became so much more pleasant to use after we started using parsers regularly. We use Runtypes in some projects and a hand rolled lib I wrote in others, and being able to parse unknown data into a known shape is a life saver. Glad to not have to write seemingly thousands of lines of interfaces and type guards any more.
- diarrhea 3y agoTwo random thoughts: - why is there `safeParse`? Why is parsing not safe by default? Have an `unsafeParse` as an escape hatch, not the other way around. - the imperative check for `success` seems unfortunate. Then again, I am familiar with pydantic from Python, which seems awfully similar to Zod, and there you'd get an Exception on failure which you'd have to catch. Some would call that uglier (I don't). Point being, I don't know of a better alternative, but sure hope native liquid/dependent types wouldn't suffer from this anymore
- mullsork 3y agoJS doesn't have any useful built-in way to deal with success/failure, other than exceptions. We use Boxed[1] for this: Result .fromExecution(() => schema.parse(input)) .mapOk(parsed_input => /* */) .mapError(parse_errors => /* */) which is an alright way to deal with things IMO. I would hazard a guess that `parse` is a function introduced long ago, and `safeParse` came afterwards. (Safe as in "do not throw an exception") [1]: https://github.com/swan-io/boxed https://github.com/swan-io/boxed
- Cannabat 3y ago`schema.parse()` is equivalent to pydantic `MyModel.parse_obj()` and throws if invalid. `const result = schema.safeParse(val)` does not throw, and if `!result.success`, `result.error` contains a `ZodError` object with all the details. It's common to further transform this error object, perhaps to make it more human-readable or suitable for an API response. Typically I find myself using `safeParse` far more than `parse`.
- shepherdjerred 3y agoYou can use `parse` which will throw an exception if the data does not match the schema, or `safeParse` if you want to manually check. > the imperative check for `success` seems unfortunate. Yes! But the cool thing is that `success` being `true` narrows the type and ensures that the `data` field is present. You cannot access `data` without checking for success Here's another example showing those concepts: https://stackblitz.com/edit/typescript-wbuikz?file=index.ts https://stackblitz.com/edit/typescript-wbuikz?file=index.ts