Zema Logo
Advanced

Branded Types

Apply nominal typing to validated values with .brand<B>(), preventing accidental mixing of semantically different values that share the same primitive type.

Dart uses structural typing, two String values are interchangeable regardless of what they represent. .brand<B>() adds a phantom type parameter B that the compiler treats as distinct, preventing accidental mixing of semantically different values at compile time with zero runtime cost.


.brand<B>()

Wraps the output in Branded<O, B>, applying nominal typing to a value that would otherwise be structurally identical to other values of the same type.

Signature:

ZemaSchema<I, Branded<O, B>> brand<B>()

Basic usage

// Two abstract marker classes, never instantiated
abstract class _UserIdBrand {}
abstract class _TeamIdBrand {}

final userIdSchema = z.string().uuid().brand<_UserIdBrand>();
final teamIdSchema = z.string().uuid().brand<_TeamIdBrand>();

final userId = userIdSchema.parse('550e8400-…');
// userId is Branded<String, _UserIdBrand>

final teamId = teamIdSchema.parse('660f9511-…');
// teamId is Branded<String, _TeamIdBrand>

void greet(Branded<String, _UserIdBrand> id) { … }
greet(teamId);   // compile-time error: wrong brand

Accessing the raw value

final userId = userIdSchema.parse(rawString);
db.fetchUser(userId.value);   // String

Branded wraps .value and delegates ==, hashCode, and toString to it, there is no runtime overhead beyond the wrapper object.


Branded equality

Two Branded values are equal if and only if they carry the same Brand type parameter and their underlying values are equal:

final a = userIdSchema.parse('abc-…');
final b = userIdSchema.parse('abc-…');
a == b;   // true; same brand, same value

Combining with other modifiers

// Branded optional
z.string().uuid().optional().brand<_UserIdBrand>()

API reference

.brand<B>()

Description
BPhantom brand type: typically an abstract class, never instantiated
ReturnsZemaSchema<I, Branded<O, B>>
Branded.valueThe underlying validated value
Copyright © 2026