Chapter 1

Ref<T>: the branded phantom type

Ref<T> is the primitive everything else is built from. It carries two things at once:

  • At compile time, a phantom type T — the type of the value at the referenced path.
  • At runtime, an array of JSONPath segments, stored under a unique symbol.

Think of it as a typed pointer into the state data. Holding a Ref<number> means the compiler knows the value at that path is a number, even though at runtime there is no number — only the string "$.width".

type WidthRef = 
type Ref<T> = {
    readonly __refType: T;
    readonly [REF_PATH]: string[];
}
A branded reference that carries both: - A phantom type `T` (compile-time only) representing the referenced value's type - A path (runtime) representing the JSONPath segments to reach it At runtime, a Ref is a Proxy object whose [REF_PATH] property returns the accumulated path segments.
Ref
<number>;
type WidthRef = {
    readonly __refType: number;
    readonly [REF_PATH]: string[];
}

__refType is the phantom brand. It never exists at runtime; it is there purely so that Ref<number> and Ref<string> are different types.

The runtime half

REF_PATH is a unique symbol used as the storage key. A symbol is used so the path can never collide with a real property name in the state data.

const const widthRef: Ref<number>widthRef = const ctx: Proxied<State>ctx.width: Ref<number>width;

const widthRef: Ref<number>widthRef[const REF_PATH: typeof REF_PATH
Symbol used as a key to store the JSONPath segments on a Ref proxy. Using a symbol prevents collisions with real property names.
REF_PATH
]; // ['$', 'width']
function pathOf(ref: Ref<unknown>): string
Extracts the JSONPath string from a Ref. Converts the internal path segments into a dot-separated JSONPath string, with array indices attached directly (no dot before brackets).
@example```ts pathOf(proxy.foo.bar) // "$.foo.bar" pathOf(proxy.items[0].name) // "$.items[0].name" ```
pathOf
(const widthRef: Ref<number>widthRef); // '$.width'

createProxy is the subject of the next chapter; for now the only thing that matters is that accessing .width produced something with a path on it. pathOf joins the segments into the JSONPath string that ASL wants.

The compile-time half

Both of these are, at runtime, the same kind of proxy object holding a path. The compiler treats them as unrelated:

type 
type State = {
    width: number;
    name: string;
}
State
= { width: numberwidth: number; name: stringname: string };
const const ctx: Proxied<State>ctx = createProxy<State>(path?: string[]): Proxied<State>
Creates a typed Proxy that records property access as JSONPath segments. Every property access on the returned proxy returns a new proxy with the property name appended to the path. Numeric keys (e.g. `[0]`) are recorded as array index segments.
@example```ts type Ctx = { foo: { bar: string[] } }; const proxy = createProxy<Ctx>(); const ref = proxy.foo.bar; pathOf(ref); // "$.foo.bar" ```
createProxy
<
type State = {
    width: number;
    name: string;
}
State
>();
const w = const ctx: Proxied<State>ctx.width: Ref<number>width;
const w: Ref<number>
const n = const ctx: Proxied<State>ctx.name: Ref<string>name;
const n: Ref<string>

That distinction is the entire point. In a payload mapping, { width: ctx.name } is a compile error while { width: ctx.width } is fine — even though both would serialize to a perfectly valid JSONPath string, and both would be accepted by ASL.

Checking at runtime

isRef is a type guard for the cases where a value could be either a ref or a plain literal — which is most payload fields, since they accept both.

function isRef(value: unknown): value is Ref<unknown>
Type guard to check whether a value is a Ref (i.e. a proxy created by createProxy).
isRef
(
const ctx: Proxied<{
    x: number;
}>
ctx
.x: Ref<number>x); // true
function isRef(value: unknown): value is Ref<unknown>
Type guard to check whether a value is a Ref (i.e. a proxy created by createProxy).
isRef
(
const ctx: Proxied<{
    x: number;
}>
ctx
); // true — the root is a Ref too
function isRef(value: unknown): value is Ref<unknown>
Type guard to check whether a value is a Ref (i.e. a proxy created by createProxy).
isRef
(42); // false
function isRef(value: unknown): value is Ref<unknown>
Type guard to check whether a value is a Ref (i.e. a proxy created by createProxy).
isRef
('$.x'); // false — a path string is not a Ref
function isRef(value: unknown): value is Ref<unknown>
Type guard to check whether a value is a Ref (i.e. a proxy created by createProxy).
isRef
(null); // false
function isRef(value: unknown): value is Ref<unknown>
Type guard to check whether a value is a Ref (i.e. a proxy created by createProxy).
isRef
({ path: stringpath: '$.x' }); // false — no REF_PATH symbol

The '$.x' case is worth noting: a hand-written path string is not a ref, and passing one where a ref is expected will not typecheck. That is deliberate — a string carries no type information, which is the problem this library exists to solve.