Coercion
Coercion schemas convert compatible inputs to a target type before validating. Access the coercion sub-namespace via z.coerce().
z.coerce().integer() // '42' → 42
z.coerce().float() // '3.14' → 3.14
z.coerce().boolean() // 'yes' → true
z.coerce().string() // 42 → '42'
z.coerce().dateTime() // '2024-01-15T10:00:00Z' → DateTime
Coercion vs strict schemas
Strict schemas accept only native Dart values. Coercion schemas accept a wider set of compatible inputs and convert them before validating.
| Input | z.integer() | z.coerce().integer() |
|---|---|---|
42 | 42 | 42 |
42.0 | invalid_type | 42 |
'42' | invalid_type | 42 |
'42.5' | invalid_type | invalid_coercion |
true | invalid_type | invalid_coercion |
z.coerce().integer()
Converts input to int. String inputs are whitespace-trimmed before parsing.
Coercion rules:
| Input | Output |
|---|---|
int | passed through |
double with no fractional part (42.0) | truncated to int (42) |
double with a fractional part (42.5) | invalid_coercion |
String parseable by int.parse ('42') | parsed integer |
String not parseable ('3.14', 'abc') | invalid_coercion |
| anything else | invalid_coercion |
final schema = z.coerce().integer();
schema.parse(42); // 42
schema.parse(42.0); // 42 (whole-number double)
schema.parse('42'); // 42
schema.parse(' 42 '); // 42 (trimmed)
schema.parse(42.5); // throws ZemaException: invalid_coercion
schema.parse('3.14'); // throws ZemaException: invalid_coercion
schema.parse(true); // throws ZemaException: invalid_coercion
Strict mode — disable string parsing:
z.coerce().integer(strict: true) // int and double only, no string parsing
z.coerce().integer(strict: true).parse('42') // throws: invalid_coercion
Range constraints — applied after coercion:
z.coerce().integer(min: 1) // >= 1 after coercion
z.coerce().integer(max: 255) // <= 255 after coercion
z.coerce().integer(min: 0, max: 100)
z.coerce().float()
Converts input to double. String inputs are whitespace-trimmed before parsing. Scientific notation ('1e3') and special values ('Infinity', 'NaN') are passed through.
Coercion rules:
| Input | Output |
|---|---|
double | passed through |
int | widened via .toDouble() |
String parseable by double.parse | parsed double |
String not parseable ('abc') | invalid_coercion |
| anything else | invalid_coercion |
final schema = z.coerce().float();
schema.parse(3.14); // 3.14
schema.parse(42); // 42.0 (int widened)
schema.parse('3.14'); // 3.14
schema.parse(' 1e3 '); // 1000.0 (trimmed, scientific notation)
schema.parse('abc'); // throws ZemaException: invalid_coercion
schema.parse(true); // throws ZemaException: invalid_coercion
Strict mode: disable string parsing:
z.coerce().float(strict: true) // double and int only
Range constraints: applied after coercion:
z.coerce().float(min: 0.0) // >= 0.0 after coercion
z.coerce().float(max: 1.0) // <= 1.0 after coercion
z.coerce().float(min: 0.0, max: 1.0) // probability range
z.coerce().boolean()
Converts input to bool. String matching is case-insensitive and whitespace-trimmed.
Coercion rules:
| Input | Output |
|---|---|
bool | passed through |
int 1 | true |
int 0 | false |
'true' '1' 'yes' 'on' | true |
'false' '0' 'no' 'off' | false |
| anything else | invalid_coercion |
final schema = z.coerce().boolean();
schema.parse(true); // true
schema.parse(1); // true
schema.parse('yes'); // true
schema.parse('ON'); // true (case-insensitive)
schema.parse(' True '); // true (trimmed)
schema.parse(false); // false
schema.parse(0); // false
schema.parse('no'); // false
schema.parse('off'); // false
schema.parse('maybe'); // throws ZemaException: invalid_coercion
schema.parse(2); // throws ZemaException: invalid_coercion
Strict mode: accept only native bool:
z.coerce().boolean(strict: true) // bool only — no int/string coercion
z.coerce().boolean(strict: true).parse(1) // throws: invalid_coercion
z.coerce().string()
Converts known primitive types to String.
Default: strict mode: only String, int, double, num, bool, and DateTime are accepted. DateTime values are converted to ISO 8601 via .toIso8601String(). Arbitrary objects are rejected to prevent unhelpful strings like "Instance of 'User'".
Coercion rules (strict mode):
| Input | Output |
|---|---|
String | passed through |
int 42 | '42' |
double 3.14 | '3.14' |
bool true | 'true' |
num | .toString() |
DateTime | .toIso8601String() |
| any other type | invalid_coercion |
final schema = z.coerce().string();
schema.parse('hello'); // 'hello'
schema.parse(42); // '42'
schema.parse(3.14); // '3.14'
schema.parse(true); // 'true'
schema.parse(DateTime(2024, 1)); // '2024-01-01T00:00:00.000'
schema.parse(Object()); // throws: invalid_coercion (not a known primitive)
Permissive mode: coerce any non-null value via .toString():
z.coerce().string(strict: false) // any object via .toString()
Chain additional constraints after coercion:
z.coerce().string().trim().min(1) // coerce, trim, then check length
z.coerce().string().email() // coerce then validate as email
z.coerce().dateTime()
Coerces multiple input representations to a Dart DateTime.
Coercion rules:
| Input | Output |
|---|---|
DateTime | passed through unchanged |
String | parsed with DateTime.tryParse (ISO 8601) |
int | DateTime.fromMillisecondsSinceEpoch(value) |
| anything else | invalid_coercion |
final schema = z.coerce().dateTime();
schema.parse(DateTime(2024, 1, 15)); // DateTime — passthrough
schema.parse('2024-01-15T10:30:00Z'); // ISO 8601 string
schema.parse(1705312200000); // Unix ms timestamp
schema.parse('not-a-date'); // throws: invalid_coercion
schema.parse(true); // throws: invalid_coercion
Range constraints — applied after coercion:
z.coerce().dateTime(after: DateTime(2000)) // on or after 2000-01-01
z.coerce().dateTime(before: DateTime.now()) // must be in the past
Tip:
z.coerce().dateTime()andz.dateTime()behave identically, both acceptString,int, andDateTime. Use whichever reads more clearly in your context.
Strict coercion summary
The strict parameter controls which input types each coercion accepts:
| Schema | Default | strict: true accepts |
|---|---|---|
z.coerce().integer() | int, double, String | int, double only |
z.coerce().float() | double, int, String | double, int only |
z.coerce().boolean() | bool, int, String | bool only |
z.coerce().string() | primitives (default strict) | same as default |
Use strict coercion when incoming data should never arrive as a string (internal services, typed APIs) but you still need the convenience of int → double widening.
Combining with modifiers
Coercion schemas extend ZemaSchema, all modifiers and transformers are available:
z.coerce().integer().optional() // null passes through
z.coerce().boolean().withDefault(false) // null → false
z.coerce().integer(min: 1).catchError((_) => 1) // failure → fallback 1
z.coerce().dateTime().nullable() // null or DateTime
Common use cases
URL query parameters
final paginationSchema = z.object({
'page': z.coerce().integer(min: 1).withDefault(1),
'perPage': z.coerce().integer(min: 1, max: 100).withDefault(20),
'active': z.coerce().boolean().withDefault(true),
'since': z.coerce().dateTime().optional(),
});
// Query string: ?page=2&perPage=50&active=true&since=2024-01-01
paginationSchema.parse({
'page': '2',
'perPage': '50',
'active': 'true',
'since': '2024-01-01T00:00:00.000Z',
});
HTML form data
final formSchema = z.object({
'age': z.coerce().integer(min: 0, max: 150),
'score': z.coerce().float(min: 0.0, max: 100.0),
'subscribe': z.coerce().boolean().withDefault(false),
'birthDate': z.coerce().dateTime(),
});
// All form values arrive as strings
formSchema.parse({
'age': '28',
'score': '95.5',
'subscribe': 'on',
'birthDate': '1996-05-20',
});
Environment variables
final envSchema = z.object({
'PORT': z.coerce().integer(min: 1, max: 65535).withDefault(8080),
'DEBUG': z.coerce().boolean().withDefault(false),
'MAX_CONNECTIONS': z.coerce().integer(min: 1).withDefault(100),
'DB_URL': z.string().url(),
});
envSchema.parse({
'PORT': Platform.environment['PORT'],
'DEBUG': Platform.environment['DEBUG'],
'MAX_CONNECTIONS': Platform.environment['MAX_CONNECTIONS'],
'DB_URL': Platform.environment['DB_URL'],
});
Error codes
| Code | Trigger |
|---|---|
invalid_coercion | Input cannot be converted to the target type. |
too_small | Coerced value is below the min bound. |
too_big | Coerced value is above the max bound. |
date_too_early | Coerced date is before the after bound. |
date_too_late | Coerced date is after the before bound. |
API reference
| Method | Parameters | Output type |
|---|---|---|
z.coerce().integer() | {int? min, int? max, bool strict = false} | int |
z.coerce().float() | {double? min, double? max, bool strict = false} | double |
z.coerce().boolean() | {bool strict = false} | bool |
z.coerce().string() | {bool strict = true} | String |
z.coerce().dateTime() | {DateTime? after, DateTime? before} | DateTime |