TypeSafe Schemas
Ack generates extension types from top-level schemas annotated with
@AckType(). The schema stays in your source file; the generator adds a typed
wrapper around its validated representation.
Overview
- Define schemas directly with the Ack fluent API.
- Annotate the top-level schema variable or getter with
@AckType(). - Run
dart run build_runner build. - Use the generated
TypeName.parse()/TypeName.safeParse()helpers.
Basic usage
import 'package:ack/ack.dart';
import 'package:ack_annotations/ack_annotations.dart';
part 'user_schema.g.dart';
@AckType()
final addressSchema = Ack.object({
'street': Ack.string(),
'city': Ack.string(),
});
@AckType()
final userSchema = Ack.object({
'id': Ack.string(),
'email': Ack.string().email().nullable(),
'address': addressSchema,
});
The generated part file contains:
AddressType(Map<String, Object?> _data)UserType(Map<String, Object?> _data)- typed getters for each field
parse()andsafeParse()factories
Supported schema shapes
@AckType() works with:
Ack.object(...)- primitive schemas such as
Ack.string(),Ack.integer(),Ack.double(),Ack.boolean() Ack.list(...)Ack.literal(...),Ack.enumString(...),Ack.enumValues(...)- non-object transforms with explicit output types
Ack.discriminated(...)with the constraints below
Unsupported helpers include Ack.any() and Ack.anyOf().
Discriminated schemas
Ack.discriminated(...) works with @AckType() when:
schemasis a non-empty map literal- the base schema is not nullable
- each branch is a top-level schema variable or getter
- each branch is an
@AckTypeobject schema - each branch is non-nullable
- each branch is declared in the same library as the base
- the branch discriminator literal matches the branch key
Example:
@AckType()
final catSchema = Ack.object({
'type': Ack.literal('cat'),
'lives': Ack.integer(),
});
@AckType()
final dogSchema = Ack.object({
'type': Ack.literal('dog'),
'breed': Ack.string(),
});
@AckType()
final petSchema = Ack.discriminated(
discriminatorKey: 'type',
schemas: {
'cat': catSchema,
'dog': dogSchema,
},
);
Resolution rules
@AckType() uses strict schema resolution.
- Nested object fields must reference a named top-level schema.
- Inline anonymous object fields are rejected for typed generation.
Ack.list(...)elements must be statically resolvable.- Cross-file references work for direct imports, prefixed imports, and re-exports.
- Unannotated object schema references fail generation instead of silently falling back to raw maps.
- Circular alias/reference chains fail generation with a clear error.
Limitations
@AckType()is only supported on top-level schema variables and getters.- Nullable top-level schemas do not emit extension types.
- List element nullability may not be preserved in all chained modifier cases.
- Use
.transform<T>(...)with an explicit output type so the generator can infer the representation type.