Learn
Types and Constraints
Primitive annotations, nullable values, unions, classes, callables, and numeric predicates.
Types and Constraints
Pkl annotations make configuration safer to edit. Use them to document shape, catch invalid values, and keep reusable modules honest.
Primitive Annotations
name: String = "api"port: Int = 8080enabled: Boolean = trueThe typechecker rejects values that do not match the declared type.
Nullable Types
Append ? to allow null.
description: String? = nullNull guards narrow the value at the point where a non-null value is required.
function label(x: String?): String = if (x != null) x else "none"Collection Types
ports: Listing<Int> = new Listing { 8080 9000}labels: Mapping<String, String> = new Mapping { ["api"] = "public"}Collection annotations make the shape of repeated values explicit.
Union Types
value: String | Int = "8080"is guards narrow unions in conditionals:
function asPort(x: String | Int): Int = if (x is Int) x else 0Type Aliases
typealias Port = Int(isBetween(1, 65535))port: Port = 8080Simple type aliases keep repeated annotations readable across values, object members, and callable boundaries.
Classes
Classes describe object contracts.
class Service { name: String port: Int = 8080}service: Service = new { name = "api"}Pkl supports class properties, defaults, typed object construction, inheritance, and method declarations / invocations. Use the official reference for the full generic class model.
Callables
Functions and lambdas can carry parameter and return annotations.
function add(x: Int, y: Int): Int = x + yinc = (x: Int): Int -> x + 1Runtime validation applies at function/lambda returns and class method returns when annotations require it.
Numeric Constraints
Supported numeric predicates include:
| Predicate | Example |
|---|---|
isBetween |
Int(isBetween(1, 65535)) |
isPositive |
Int(isPositive) |
isGreaterThan |
Int(isGreaterThan(0)) |
isLessThan |
Int(isLessThan(10)) |
| negation | Int(!isPositive) |
Multiple numeric constraints can be listed in one annotation.
replicas: Int(isPositive, isLessThan(10)) = 3User-defined predicate factories keep threshold-dependent rules close to the domain language.
const function above(n) = (x) -> x > nreplicas: Int(above(0)) = 3The same pattern applies to richer String, Float, Regex, Duration, DataSize, and collection-element constraints.