Skip to content

Recursive Types

If you want an object type to be able to contain one of itself (recursion), then you have to start using keyed types. The basic pattern is this:

/**
* Wrapping a schema with a 'bin.keyed' call allows the inner code to
* use a reference to the type we're currently creating, instead
* of the type itself.
*
* The reference variable 'Recursive' doesn't have to be called
* the same as the actual variable we're storing the schema in,
* but it's a neat trick that makes the schema code more readable.
*
* The 'recursive-key' has to uniquely identify this type in this tree.
* There may be other distinct types using the same key, as long as they do
* not interact with each other (one doesn't contain the other).
* This is because references are resolved recursively once the method
* passed as the 2nd argument to 'keyed' returns the schema.
*/
const Recursive = bin.keyed('recursive-key', (Recursive) =>
bin.object({
value: bin.i32,
next: bin.optional(Recursive),
})
);

Recursive types alongside generics

import bin from 'typed-binary';
const Expression = bin.keyed('expression', (Expression) =>
bin.generic(
{},
{
multiply: bin.object({
a: Expression,
b: Expression,
}),
negate: bin.object({
inner: Expression,
}),
int_literal: bin.object({
value: bin.i32,
}),
}
)
);
type Expression = bin.Parsed<typeof Expression>;
const expr: Expression = {
type: 'multiply',
a: {
type: 'negate',
inner: {
type: 'int_literal',
value: 15,
},
},
b: {
type: 'int_literal',
value: 2,
},
};