Types
Lazy
Use ft.Lazy to validate an unknown value matches a recursive data structure.
LinkedList
import * as ft from "funtypes";
interface LinkedList<T> {
value: T;
next: LinkedList<T> | null;
}
const LinkedListCodec = <T>(value: ft.Codec<T>) => {
const ListNode = ft.Lazy(
(): ft.Codec<LinkedList<T>> =>
ft.Object({
value,
next: ft.Union(ft.Null, ListNode),
}),
);
return ListNode;
};
const NumberLinkedListCodec = LinkedListCodec(ft.Number);
// ✅ Valid nested structure
assert.deepEqual(
NumberLinkedListCodec.parse(
{ value: 1, next: { value: 2, next: null } },
)
{ value: 1, next: { value: 2, next: null } }
)
// ✅ Cycles in these recursive structures are ok
const NumberLoop = { value: 1, next: { value: 2, next: null } }
NumberLoop.next.next = NumberLoop;
assert.deepEqual(
NumberLinkedListCodec.parse(NumberLoop),
NumberLoop,
);
// 🚨 Nested value is invalid because v.next.next is `false`
assert.throws(() => {
NumberLinkedListCodec.parse(
{ value: 1, next: { value: 2, next: false } },
);
})
TypeScript can't infer recursive types
TypeScript will check our working, but it can't do inference when the code is recursive like this, so we have to manually specify the return type on the function we pass in to ft.Lazy.