API reference › @evolu/common › Type › lazy
function lazy<Target>(
getType: Thunk<ValidateLazyTarget<Target>>,
): LazyType<
Target["Input"],
Target["Output"],
TypeFromError<Target>,
InferErrors<RootType<Target>>,
InferErrors<Target>,
CanonicalInputOf<Target>,
IdentityEncodingOf<Target>
>;
Defined in: packages/common/src/Type.ts:12876
Creates a lazy Type for recursive definitions.
The definition is evaluated on first use and then cached, allowing recursive data such as trees and mutually recursive models.
A recursive declaration refers to its own variable while that variable is being initialized, so TypeScript cannot infer it reliably. Getter-based inference tricks are brittle once optional properties, unions, or mutually recursive definitions are involved. Declare recursive data interfaces and structured error types explicitly for stable inference and clearer compiler errors.
The definition must return one concrete non-Lazy Type. Use union for alternatives. Every recursive Lazy reference must be nested behind an object, array, tuple, record, set, or map structural boundary. Union does not guard recursion because it passes the same value to every member. Lazy defers schema construction; it does not make cyclic runtime object graphs or arbitrarily deep values stack-safe.
Example
import {
assertOk,
assertType,
String,
array,
lazy,
object,
type ArrayError,
type LazyType,
type ObjectError,
type TypeOfError,
} from "@evolu/common";
interface Tree {
readonly value: string;
readonly children: ReadonlyArray<Tree>;
}
interface TreeError extends ObjectError<{
readonly value: TypeOfError<"String">;
readonly children: ArrayError<TreeError>;
}> {}
const Tree: LazyType<Tree, Tree, never, TreeError, TreeError> = lazy(() =>
object({ value: String, children: array(Tree) }),
);
const result = Tree.fromUnknown({
value: "root",
children: [{ value: "leaf", children: [] }],
});
assertOk(result, {
value: "root",
children: [{ value: "leaf", children: [] }],
});
assertType<Tree, typeof result.value>();