Using Zod Schemas as a Source of Truth for Typescript Types
Learn how to avoid duplicating both Zod Schemas and Typescript Types and instead infer Types from Zod Schemas, using them as a source of truth for your Types.
When building apps using Typescript, runtime validation is usually an afterthought. Typescript types are removed during the transpilation process, and while they help with static type checking, they don’t help with dynamic data, such as data from APIs, Forms, etc.
This is where schema validation libraries, such as Zod, Yup, etc., come in. They allow us to validate our data at runtime and ensure that we plug another possible source of bugs in our system.
Unfortunately, we have defined our types twice for most of these libraries, which can be a maintenance nightmare. A change in the shape of our data means that both types (Typescript and Schema validation types) must be updated.
What if we could create one and automatically get the other, i.e., write a JSON schema validation and then automatically get Typescript Types? This is where Zod comes in.
Introduction to Zod
Zod is a TypeScript-first schema declaration and validation library. I'm using the term "schema" to broadly refer to any data type, from a simple
string
to a complex nested object.
With Zod, we can define our schema, which could be any data type, and then we can get Typescript types from the schema, allowing us to use it as the source of truth. If you are interested in why you use Zod, please check out this previous issue I did a while back.
Let’s see a few examples. We will start with a string and then build our examples from there. In Zod, we can define a runtime validation of a string as follows:
With the above example, we can get our type as follows:
And just like that, we get a type we can use within our Typescript code, inside an argument, variable definitions, etc.
Working with Objects in Zod
Let’s move on to another more realistic example you will probably encounter more often than not. Let’s define a schema
validation object for a theoretical user that has the following fields: id
, name
, email
, password
, role
, phone
, where the phone is optional, and the role is an enum.
And now, we can get our types, just like we previously did.
And this is the type we get back:
Sweet, isn’t it?
If you like my content and want to support my work, please consider supporting me (you can buy me a double latte, the juice that powers my creativity and determination ☕️) through Github Sponsors.
Manipulating Schemas and Types
On top of defining schemas, we can manipulate existing Zod schemas using methods similar to Typescript Utility Types - such as Omit
, Pick
, etc.
For instance, from the above user schema, we can drop the id field if, for instance, to represent a new user object, before we assign them a user id.
And in return, we get the following Typescript type:
On top of omit, Zod provides the following Typescript Utility Type inspired methods:
and more.
For more information, check out the official docs here.
Conclusion
In this issue, we learned that we could use Zod as the source of truth for our types and get types back. We learned how to use Zod schema to get both runtime validation schemas and types for Typescript for static Typing. This should help us write fewer lines of code, make it easy to maintain our Types and sync our types and schema, and get more Type safety, leading to fewer lines of code.
That's it for this issue. Thank you for getting this far. If you enjoyed this article and would like to support my work, please share and like this issue and consider sharing All Things Typescript with friends and colleagues.
Did you know you can hire me to coach your team and help them improve their Typescript skills? If so, please contact me to discuss it.
And until next time, please keep on learning.