Zema Logo
Transformations

Coercion

Convert loosely-typed inputs to Dart types before validation with z.coerce(): integer, float, boolean, string, and dateTime.

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.

Inputz.integer()z.coerce().integer()
424242
42.0invalid_type42
'42'invalid_type42
'42.5'invalid_typeinvalid_coercion
trueinvalid_typeinvalid_coercion

z.coerce().integer()

Converts input to int. String inputs are whitespace-trimmed before parsing.

Coercion rules:

InputOutput
intpassed 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 elseinvalid_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:

InputOutput
doublepassed through
intwidened via .toDouble()
String parseable by double.parseparsed double
String not parseable ('abc')invalid_coercion
anything elseinvalid_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:

InputOutput
boolpassed through
int 1true
int 0false
'true' '1' 'yes' 'on'true
'false' '0' 'no' 'off'false
anything elseinvalid_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):

InputOutput
Stringpassed through
int 42'42'
double 3.14'3.14'
bool true'true'
num.toString()
DateTime.toIso8601String()
any other typeinvalid_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:

InputOutput
DateTimepassed through unchanged
Stringparsed with DateTime.tryParse (ISO 8601)
intDateTime.fromMillisecondsSinceEpoch(value)
anything elseinvalid_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() and z.dateTime() behave identically, both accept String, int, and DateTime. Use whichever reads more clearly in your context.


Strict coercion summary

The strict parameter controls which input types each coercion accepts:

SchemaDefaultstrict: true accepts
z.coerce().integer()int, double, Stringint, double only
z.coerce().float()double, int, Stringdouble, int only
z.coerce().boolean()bool, int, Stringbool 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

CodeTrigger
invalid_coercionInput cannot be converted to the target type.
too_smallCoerced value is below the min bound.
too_bigCoerced value is above the max bound.
date_too_earlyCoerced date is before the after bound.
date_too_lateCoerced date is after the before bound.

API reference

MethodParametersOutput 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
Copyright © 2026