Literals
Union types with specific values, broad types, and structured variants
Literals define union types for fields — a single field that can hold one of several specific values or structured types. They're more flexible than enums: where enums are limited to named string constants, literals can mix strings, numbers, booleans, broad types, objects, tuples, and references to other literals or enums.
Defining a Literal
Declare a literal with the literal keyword followed by a name and a comma-separated list of variants:
literal Status { 'active', 'inactive', 'pending' }
literal Priority { 1, 2, 3 }
literal Mixed { 'low', 'high', 1, 2, true }Variants can also be written on separate lines, one per line without commas. Each variant represents one allowed value or type that the field can hold.
Variant Kinds
Literals support nine kinds of variants:
| Kind | Example | Description |
|---|---|---|
| String | 'active' | An exact string value |
| Integer | 1, 42 | An exact integer value |
| Float | 3.14 | An exact float value |
| Boolean | true, false | An exact boolean value |
| Broad type | String, Int, Float, Bool, Date | Any value of that type |
| Object ref | MyObject | A defined object type |
| Tuple ref | MyTuple | A defined tuple type |
| Literal ref | OtherLiteral | Variants from another literal (expanded inline) |
| Enum ref | MyEnum | Variants from an enum (expanded as string values) |
String values are quoted with single quotes. Numbers and booleans are bare values. Type names and references use PascalCase identifiers.
Broad Types
Broad type variants accept any value of that type, not just specific values:
literal Flexible { String, Int }A field of type Flexible accepts any string or any integer:
await client.db.Model.create({
data: { value: 'anything' }, // OK — any string
});
await client.db.Model.create({
data: { value: 12345 }, // OK — any integer
});The five broad types are String, Int, Float, Bool, and Date.
Composition
Literals can include variants from other literals or enums. The referenced type's values are expanded and deduplicated into the parent literal:
literal Status { 'active', 'inactive', 'pending' }
literal ExtendedStatus { Status, 'archived', 'deleted' }
# ExtendedStatus = 'active' | 'inactive' | 'pending' | 'archived' | 'deleted'enum Role { ADMIN, EDITOR, VIEWER }
literal RoleOrCustom { Role, 'custom' }
# RoleOrCustom = 'ADMIN' | 'EDITOR' | 'VIEWER' | 'custom'When a literal references an enum, the enum's string values are inlined into the literal's union. This lets you build on existing enums when you need a broader set of values.
Literals also support extends for inheritance (distinct from inline composition). The child literal inherits all variants from the parent and can add new ones. See the Extends page for details.
Object Variants
Literals can include object types as variants, creating a union of primitive values and structured data:
object Point {
x Float
y Float
}
literal Shape { 'none', Point }// Create with string variant
await client.db.Model.create({
data: { shape: 'none' },
});
// Create with object variant — all required fields must be present
await client.db.Model.create({
data: { shape: { x: 1.5, y: 2.5 } },
});Object fields inside literals support optional (?) and nullable (@nullable) modifiers:
object PointOpt {
label String
x Float?
y Float? @nullable
}
literal ShapeOpt { 'none', PointOpt }Decorator restrictions
Only ? and @nullable are enforced when an object is stored through a literal. Other decorators (@default, @createdAt, @updatedAt, @readonly) cannot be expressed in the inline type definition and will not apply through the literal path. A warning is shown during generation if decorated objects are used in literals.
The same object can be used both in a literal and directly on a model field — decorators apply normally when used directly.
Tuple Variants
Literals can include tuple types as variants:
tuple Coord {
x Float,
y Float
}
literal Position { 'origin', Coord }// String variant
await client.db.Model.create({
data: { pos: 'origin' },
});
// Tuple variant — array form
await client.db.Model.create({
data: { pos: [1.5, 2.5] },
});
// Tuple variant — object form (named elements)
await client.db.Model.create({
data: { pos: { x: 1.5, y: 2.5 } },
});Nesting Restrictions
Objects and tuples used in literals must be flat — they can only contain:
- Primitive fields (
String,Int,Float,Bool,Date) - Literal-typed fields (if the inner literal contains only primitive variants)
Flat objects and tuples with only primitive fields are valid:
object Coordinate {
x Float
y Float
}
literal SimpleValue { 'text', 42 }
# Allowed — object with only primitives
literal Shape { 'circle', 'square', Coordinate }
object TaggedPoint {
label String
kind SimpleValue # OK: SimpleValue only has primitive variants
}
literal Data { TaggedPoint, 'raw' }Objects with nested objects, tuples, or complex literal fields are rejected at parse time:
object Inner {
value String
}
object Outer {
nested Inner # has an object sub-field
}
# REJECTED — Outer contains a nested object field
literal Bad { Outer, 'fallback' }
literal Complex { 'a', Int, AnotherLiteral }
object DeepRef {
tag Complex # Complex contains literalRef to AnotherLiteral
}
# REJECTED — DeepRef's literal field references a non-simple literal
literal AlsoBad { DeepRef, 'none' }Objects with nested object fields, tuple fields, or complex literal fields inside a literal are rejected at parse time. This restriction also applies to tuple elements — only primitives and simple literal-typed elements are allowed.
Literals referenced inside another literal's object/tuple variants must themselves contain only simple variants (no objectRef, tupleRef, or literalRef). Deep nesting is not supported.
Using Literals on Fields
Literal types are used on fields just like any other type. They support optionality, nullability, defaults, and arrays:
literal Status { 'active', 'inactive', 'pending' }
literal Priority { 1, 2, 3 }
model Task {
id Record @id
status Status # required
priority Priority @default(1) # required with default
prevStatus Status? # optional
nullStatus Status? @nullable # optional + nullable
tags Status[] # array of literal values
}Generated TypeScript
For a literal with only primitive variants, Cerial generates a single union type:
literal Status { 'active', 'inactive', 'pending' }export type Status = 'active' | 'inactive' | 'pending';For a literal with object or tuple variants, both an output type and an input type are generated:
object Point {
x Float
y Float
}
literal Shape { 'none', Point }// Output type — returned from queries
export type Shape = 'none' | Point;
// Input type — used in create/update
export type ShapeInput = 'none' | PointInput;Key points about generated types:
- Output uses base types (
Point), input uses input variants (PointInput) - For tuples: output is always array form, input accepts both array and object form
- A separate
Inputtype is only generated when the literal contains object or tuple variants