Explained: 'React' refers to a UMD global
Find out why this error occurs and learn how to fix it.
You're an engineer. You're building something. The thing you're building probably has inputs - points at which data is injected.
Let's say you're building a CLI called matt
:
text
matt run <something>
Here, <something>
is the input. It's the thing that tells the program what to do. It's totally unknown at runtime - it might not even exist.
If you're building a public-facing API, you might have inputs that are exposed to public ports on the web:
text
GET https://mattpocock.com/api/user/:idPOST https://mattpocock.com/api/user/:id
Several possible inputs come to mind:
:id
parameter on the pathGET
or POST
)?hello=world
POST
)All of these are unknown at runtime, because if these API's are exposed to the world anyone can ping them. If they aren't validated, many might even be vectors for attack.
In each of these cases, you don't trust the data entering your app. For those cases, you should use Zod.
For the script, you could parse the process.argv
:
typescript
import {z} from 'zod'// Make a schema for the argumentsconst ArgSchema = z.tuple([z.any(), z.any(), z.string()])// Use it to parse process.argvconst [, , name] = ArgSchema.parse(process.argv)// Log it to the console safely!console.log(name)
process.argv
usually contains two useless arguments, then the dynamic one you want to extract. For more complex cases, you'll want to use commander
- but for simple scripts this works great.
The nice thing about using Zod here is that name
is inferred as a string
without any other work needed.
For your public API, you can create Zod schemas to ensure that the request body and headers are correct.
typescript
import {z} from 'zod'import {Request, Response} from 'express'const CreateUserSchema = z.object({body: z.object({// Ensures that the email exists, and is an emailemail: z.string().email(),}),headers: z.object({// Ensures that the authorization header is presentauthorization: z.string(),}),})const handleCreateUser = (req: Request, res: Response) => {// Parse the requestconst result = CreateUserSchema.safeParse(req)// If something was missing, send back an errorif (!result.success) {res.status(400).send(result.error)return}const {email} = result.data.body// Create the user}
We use .safeParse
here so that we don't throw an error - instead, we return 400
and pass back the error. Zod throws really nice, readable errors, so we can be sure the user knows what went wrong.
Thanks to Zod, you can be sure that the unknown inputs in your app are validated and safe. If I were building an app with any unknown inputs, I'd add Zod right away.
Some more examples of unknown inputs:
localStorage
- users can manipulate this, or it might be out of date.Zod's use cases are obvious for untrustworthy inputs to your application. But there are other types of inputs which you 'sort of' trust.
The example that comes to mind is third-party services. If your app relies on calling a third-party API which you don't control, should you validate that API with Zod?
If that API changes its shape, that might cause subtle bugs in your application. I've been through this plenty of times as an engineer: assuming for hours that my code is wrong before realising that the API returned something I didn't expect.
Validating that data with Zod will still cause an error in your app - but that error will be thrown early, right when the data enters your app. This makes it much easier to debug and fix.
In that case, why not validate? If bundle size is a concern, Zod is 12kb gzipped which is a little too large for some apps. Validation is also, inevitably, slightly slower than not validating. So if critical-path performance is a concern, you might want to skip Zod.
However, in most apps I've built in my career, robustness is the key concern. 'Impossible data' - or data you don't expect - has been probably the most frequent cause of bugs throughout my dev life. So I'll be validating any input that's remotely untrustworthy.
Some more 'sort of' trustworthy inputs:
Let's take the final case. Imagine you're building a fullstack app, using a popular framework like Remix, Next.js, SvelteKit or Nuxt.
You want to load some data from the frontend. You ping an API endpoint (which, since it's public-facing, is likely already validated with Zod). You get some data back. Should you validate that data with Zod on the frontend?
This is a tricky one. We completely control the API endpoint - we're in charge of deploying to it, and it's deployed in sync with the frontend. However - it's still possible that the following sequence happens:
This kind of 'version drift' between frontend and backend is more common than you think, especially given how often some teams deploy. If we had Zod on the frontend, we'd be able to error as soon as any kind of version drift happened and prompt the user to refresh the page.
However, in these situations I usually opt for not using Zod. With version drift, the app is usually just a browser refresh away from a better experience. Since nothing security-sensitive is exposed to frontend code, the blast radius of a bug is relatively small.
This is up for debate, though - you may want to check all the data coming into your app to error whenever a version drift happens.
When your app has inputs you don't trust, use Zod.
When your app has inputs you trust but don't control, validate them with Zod.
When your app has inputs you trust and control, I usually don't validate them with Zod.
If you want to learn more about using Zod, check out my free, 10-exercise tutorial on Zod.
Share this article with your friends
Find out why this error occurs and learn how to fix it.
Learn the differences between React.ReactNode
and JSX.Element
in TypeScript when working with React.
Since I first got into advanced TypeScript, I've been in love with a particular pattern. It formed the basis for one of my first-ever TypeScript tips, and it's been extraordinarily useful to me ever since. I call it the IIMT (rhymes with 'limped'): the Immediately Indexed Mapped Type.
There are three rules to keep in mind when deciding where to put types in your application code.
Discover the power of ComponentProps in React and TypeScript. Learn how to extract and utilize props from native HTML elements, existing components, and elements with associated refs. Enhance your development workflow with this essential type helper.
Testing types is a crucial aspect of developing libraries in TypeScript. In this article, we explore three different ways to test your types: using the vitest test runner, rolling your own solution with type helpers, and leveraging the tsd library.
The TypeScript 5.1 beta is out - here's everything you need to know.
There’s a difference between using TypeScript and knowing TypeScript.
The docs give you a good grasp of the pieces like generic functions, conditional types, and type helpers.
But out in the wild, developers are combining these pieces together into patterns.
Four of the most important patterns to know and use are:
Testing code doesn't need to be typed so strictly, and sometimes tests need to pass the wrong type. I made a library called shoehorn that eases the pain of working with tests in TypeScript by providing a first-class way to pass the wrong type to a function.
The article discusses why TypeScript does not throw an error when a function that is assigned to a variable doesn't match its type. It explains that a function with fewer parameters than its type can still be passed, and this behavior is not restricted to TypeScript but exists in JavaScript as well.
TypeScript 5.0 introduces const type parameters which are useful in preserving the literal types of objects passed to functions.
Updates to TypeScript 5.0 have made their way into Total TypeScript!
Exclude
is a very powerful utility type that can be used in a variety of ways. In this article, I'll show you 9 ways to use it along with code examples.
As a frontend developer, your job isn't just pixel-pushing. Most of the complexity in frontend comes from handling all the various states your app can be in.
It might be loading data, waiting for a form to be filled in, or sending a telemetry event - or all three at the same time.
If you aren't handling your states properly, you're likely to come unstuck. And handling states starts with how th
Using the satisfies keyword is one of four ways to make type assignments in TypeScript. In this article we'll look at examples of when each method should be used.
Understand why TypeScript throws complicated errors by learning how to read them. Errors mirror the structure of the code being compared and can be simplified by changing the way types are assigned.
Learn how to use TypeScript generics on the type level and with functions to improve code readability, type safety, and reduce repetitive code. Use "type helpers" to create new types and generic functions to pass in and return specific types.
TypeScript's template literal syntax allows developers to manipulate and transform strings in a powerful way. This can be extended using unions, infer, and recursion to handle more complex tasks.
Donny (kdy1 on GitHub) is rewriting TypeScript in Rust hoping to speed up tsc which is slow due to its TypeScript base. In this interview, he explains some of the financial and technical challenges they face with this open-source project.
Let's imagine you're creating a function which sums up an array of objects. Here's one, taken from the Excalidraw codebase:
const sum = <T>(array: readonly T[], mapper: (item: T) => number): number => array.reduce((acc, item) => acc + mapper(item), 0)
Let's look at the type definition. This function takes in:
readonly T[]