Tom MacWright

2025@macwright.com

Maybe rich parsers are the way to introduce rich types

Chris Krycho's Friendly Little Wrapper Types article reminded me of my own One Way To Represent Things article from 2021. That idea has been bouncing around my head ever since.

Basically "rich types." There are lots of places where an email or a color is represented as a string, and a duration is represented as an integer, but what if they weren't? It would help with type-safety and open up the opportunity for more convenient methods: color.toRGB() or toRGB(color) without having to re-parse everything every time. How do we get to this reality?

Reflecting on both pieces makes me wonder about parsing as the lever for adoption. Chris's piece has an example of an email type that you'd construct and that would give you some structured errors if it wasn't correctly formatted.

The rise of Zod and other parsers that give you both typesafety and runtime assurances is one of the biggest changes in JavaScript/TypeScript in years. I remember the bad old days when we'd have to manually parse inputs, use jsonschema for everything, and when a lot of applications were just naïve about the shape of their input data. Now, basically everything uses Zod or something like it, and it's a huge lever: Zod is what makes tRPC possible with so little boilerplate.

So the idea is: what if parser libraries produced branded types by default? Zod already supports basic typesystem-only branded types, and so does Effect. Maybe there's an opportunity for a higher-level library built on one of these that brands types like UUIDs, emails, colors, IP addresses, passwords, usernames, and more by default?

This could enable something like

import p from "parser";

const type = p.object({ color: p.color(), uuid: p.uuid() });

const result = type.parse({
	color: '#ff00ff',
	uuid: '5426a36a-bfd4-46ff-9066-c7b5fac0bf9c',
	email: 'foo@bar.com'
});

// Color methods
result.color.hsl();
// Or in functional style, but checking that the brand is right
toHSL(result.color);

// UUID methods
result.uuid.version();
uuidVersion(result.uuid);

Wouldn't that be nice?

It does dawn on me now that what I'm talking about are codecs. Effect's Schema is already a codec-based system, but it's a new idea to Zod. Maybe we just start using that a lot more?