Skip to main content
Mohammad Shehadeh — home (MSH monogram, letter M filled with the Palestinian flag)

How I use Zod with Wordle Unlimited

Published on
2 min read

Type safety matters whenever data goes over the network, lives in localStorage, or sits anywhere else we don't fully control.

Let's say we built a strictly typed way to store data in localStorage:

1type Colors = 'red' | 'green' | 'blue';
2
3const key = 'colors'; const data: Colors[] = ['green', 'red'];
4
5localStorage.setItem(key, JSON.stringify(data));

We have no control over what happens to the data, so it sits outside our type system. A user can open their developer tools and alter it. Once that happens, there's no guarantee the data still satisfies the Colors type.

We have types, but that's not the same as having type safety.

A quick intro into Zod

Zod validates data in TypeScript applications. It enforces precise data structures, making sure incoming data matches the expected types and shapes. For more, see How to validate data using Zod

A quick intro to Wordle Unlimited

Wordle Unlimited is a guessing game with 6 tries. After each guess, the tile colors change to show how close your guess was to the word. Each guess must be a valid word with the same number of letters as the squares in a row.

Example:

f
i
r
s
t

We want to save game progress in the browser localStorage while type-checking the stored values. This guards against game-breaking issues if those values are altered.

Validation with Zod

The schema defines the structure and validation rules for the Wordle data type:

1export const GameSchema = z.object({
2  currentGuess: z.string(),
3  isWon: z.boolean(),
4  isLost: z.boolean(),
5  invalidGuess: z.boolean(),
6  guesses: z.array(z.array(z.string())),
7  solution: z.string(),
8});

To avoid code duplication we can extract the TypeScript type of GameSchema using z.infer:

1type GameState = z.infer<typeof GameSchema>;

To get the data back, we use safeParse. It returns an object with either the parsed data or a ZodError instance holding detailed validation information:

1const data = localStorage.getItem(WORDLE_LOCAL_STORAGE_KEY);
2const results = GameSchema.safeParse(data);
3
4if (!results.success) {
5// initialize the game with the default state 
6} else {
7// initialize the game with successfully parsed data "results.data" 
8}

This lets us use Zod to validate external data that falls outside our type system.

Quick Recap

  1. Type Safety Beyond TypeScript

    • TypeScript types only exist at compile time
    • Runtime validation is crucial for external data
    • Zod bridges the gap between compile-time and runtime type safety
  2. Data Validation Best Practices

    • Always validate external data sources
    • Use safeParse for graceful error handling
    • Leverage z.infer for type inference
    • Create reusable schemas for common patterns
  3. Real-World Applications

    • Form validation
    • API response validation
    • Local storage data validation
    • Configuration validation

References

Related Articles

GET IN TOUCH

Let's work together

I build fast, accessible, and delightful digital experiences for the web. Whether you have a project in mind or just want to connect, I'd love to hear from you.

Get in touch

or reach out directly at hello@mohammadshehadeh.com