Chapter 6

Intrinsic functions

Step Functions has built-in "intrinsic functions" — States.Format(), States.JsonToString(), States.MathAdd() and friends. They run inside the state machine rather than in a Lambda, which makes them the cheap way to reshape data between states.

They are wrapped as IntrinsicExpr<T>, following the same pattern as Ref<T>: a phantom type at compile time, an expression string at runtime. And because IntrinsicExpr<T> is accepted anywhere Ref<T> is, they drop into payloads without ceremony.

States.Format

Interpolates {} placeholders. It always yields a string, so the type is IntrinsicExpr<string> regardless of what goes in:

const 
const ctx: Proxied<{
    sceneId: string;
    frameIndex: number;
}>
ctx
=
createProxy<{
    sceneId: string;
    frameIndex: number;
}>(path?: string[]): Proxied<{
    sceneId: string;
    frameIndex: number;
}>
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
<{ sceneId: stringsceneId: string; frameIndex: numberframeIndex: number }>();
const expr = function statesFormat(template: string, ...args: (Ref<unknown> | IntrinsicExpr<unknown>)[]): IntrinsicExpr<string>
Step Functions `States.Format()` intrinsic function. Produces a string by interpolating `{}` placeholders in the template with the provided arguments (refs or other intrinsics). Single quotes in the template are escaped automatically. To include a literal `{` or `}`, escape it yourself as `\\{` / `\\}` per the ASL spec.
@example```ts statesFormat('scene_{}/frame', item.value.id) // → "States.Format('scene_{}/frame', $$.Map.Item.Value.id)" ```
statesFormat
('scene_{}/frame_{}',
const ctx: Proxied<{
    sceneId: string;
    frameIndex: number;
}>
ctx
.sceneId: Ref<string>sceneId,
const ctx: Proxied<{
    sceneId: string;
    frameIndex: number;
}>
ctx
.frameIndex: Ref<number>frameIndex);
const expr: IntrinsicExpr<string>

At runtime that is States.Format('scene_{}/frame_{}', $.sceneId, $.frameIndex). With no arguments it is just a template: statesFormat('hello world').

Conversions and arithmetic

const 
const ctx: Proxied<{
    data: {
        scenes: unknown[];
    };
    scene: {
        startFrame: number;
    };
}>
ctx
= createProxy<{
data: {
    scenes: unknown[];
}
data
: { scenes: unknown[]scenes: unknown[] };
scene: {
    startFrame: number;
}
scene
: { startFrame: numberstartFrame: number };
}>(); function statesJsonToString(ref: Ref<unknown> | IntrinsicExpr<unknown>): IntrinsicExpr<string>
Step Functions `States.JsonToString()` intrinsic function. Converts a JSON value to its string representation.
@example```ts statesJsonToString(ctx.extractScenes) // → "States.JsonToString($.extractScenes)" ```
statesJsonToString
(
const ctx: Proxied<{
    data: {
        scenes: unknown[];
    };
    scene: {
        startFrame: number;
    };
}>
ctx
.
data: Proxied<{
    scenes: unknown[];
}>
data
);
// States.JsonToString($.data) → IntrinsicExpr<string> function statesMathAdd(ref: Ref<number> | IntrinsicExpr<number>, operand: number): IntrinsicExpr<number>
Step Functions `States.MathAdd()` intrinsic function. Adds an integer operand to a numeric value referenced by a JSONPath.
@example```ts statesMathAdd(ctx.scene.start_frame, 1) // → "States.MathAdd($.scene.start_frame, 1)" ```
statesMathAdd
(
const ctx: Proxied<{
    data: {
        scenes: unknown[];
    };
    scene: {
        startFrame: number;
    };
}>
ctx
.
scene: Proxied<{
    startFrame: number;
}>
scene
.startFrame: Ref<number>startFrame, 1);
// States.MathAdd($.scene.startFrame, 1) → IntrinsicExpr<number>

statesStringToJson goes the other way. Since the parsed shape can't be inferred from a string, you supply it:

const 
const ctx: Proxied<{
    rawJson: string;
}>
ctx
=
createProxy<{
    rawJson: string;
}>(path?: string[]): Proxied<{
    rawJson: string;
}>
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
<{ rawJson: stringrawJson: string }>();
const parsed =
statesStringToJson<{
    id: string;
}>(ref: Ref<string> | IntrinsicExpr<string>): IntrinsicExpr<{
    id: string;
}>
Step Functions `States.StringToJson()` intrinsic function. Parses a JSON string into a value. Pass a type parameter to describe the parsed shape.
@example```ts statesStringToJson<{ id: string }>(ctx.rawJson) // → "States.StringToJson($.rawJson)" ```
statesStringToJson
<{ id: stringid: string }>(
const ctx: Proxied<{
    rawJson: string;
}>
ctx
.rawJson: Ref<string>rawJson);
const parsed: IntrinsicExpr<{
    id: string;
}>

Telling them apart

isIntrinsic distinguishes an intrinsic from a ref or a plain value. Note that a ref is not an intrinsic — they are separate brands that happen to serialize the same way:

const const expr: IntrinsicExpr<string>expr = function statesFormat(template: string, ...args: (Ref<unknown> | IntrinsicExpr<unknown>)[]): IntrinsicExpr<string>
Step Functions `States.Format()` intrinsic function. Produces a string by interpolating `{}` placeholders in the template with the provided arguments (refs or other intrinsics). Single quotes in the template are escaped automatically. To include a literal `{` or `}`, escape it yourself as `\\{` / `\\}` per the ASL spec.
@example```ts statesFormat('scene_{}/frame', item.value.id) // → "States.Format('scene_{}/frame', $$.Map.Item.Value.id)" ```
statesFormat
('{}',
const ctx: Proxied<{
    x: number;
}>
ctx
.x: Ref<number>x);
function isIntrinsic(value: unknown): value is IntrinsicExpr
Type guard to check whether a value is an IntrinsicExpr.
isIntrinsic
(const expr: IntrinsicExpr<string>expr); // true
function isIntrinsic(value: unknown): value is IntrinsicExpr
Type guard to check whether a value is an IntrinsicExpr.
isIntrinsic
(
const ctx: Proxied<{
    x: number;
}>
ctx
.x: Ref<number>x); // false — a Ref
function isIntrinsic(value: unknown): value is IntrinsicExpr
Type guard to check whether a value is an IntrinsicExpr.
isIntrinsic
('hello'); // false

They compose

An intrinsic is accepted wherever a ref is, including inside another intrinsic:

const 
const ctx: Proxied<{
    data: unknown;
}>
ctx
=
createProxy<{
    data: unknown;
}>(path?: string[]): Proxied<{
    data: unknown;
}>
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
<{ data: unknowndata: unknown }>();
const const inner: IntrinsicExpr<string>inner = function statesJsonToString(ref: Ref<unknown> | IntrinsicExpr<unknown>): IntrinsicExpr<string>
Step Functions `States.JsonToString()` intrinsic function. Converts a JSON value to its string representation.
@example```ts statesJsonToString(ctx.extractScenes) // → "States.JsonToString($.extractScenes)" ```
statesJsonToString
(
const ctx: Proxied<{
    data: unknown;
}>
ctx
.data: Ref<unknown>data);
const const outer: IntrinsicExpr<string>outer = function statesFormat(template: string, ...args: (Ref<unknown> | IntrinsicExpr<unknown>)[]): IntrinsicExpr<string>
Step Functions `States.Format()` intrinsic function. Produces a string by interpolating `{}` placeholders in the template with the provided arguments (refs or other intrinsics). Single quotes in the template are escaped automatically. To include a literal `{` or `}`, escape it yourself as `\\{` / `\\}` per the ASL spec.
@example```ts statesFormat('scene_{}/frame', item.value.id) // → "States.Format('scene_{}/frame', $$.Map.Item.Value.id)" ```
statesFormat
('payload: {}', const inner: IntrinsicExpr<string>inner);
// States.Format('payload: {}', States.JsonToString($.data))

States.Array — the array escape hatch

Serialization rejects a bare ref used as an array element, because ASL has no path substitution inside arrays. statesArray is how you build one:

const 
const ctx: Proxied<{
    a: string;
    b: string;
}>
ctx
=
createProxy<{
    a: string;
    b: string;
}>(path?: string[]): Proxied<{
    a: string;
    b: string;
}>
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
<{ a: stringa: string; b: stringb: string }>();
const arr = statesArray<string>(...items: (string | Ref<string> | IntrinsicExpr<string>)[]): IntrinsicExpr<string[]>
Step Functions `States.Array()` intrinsic function. Builds an array from refs, intrinsics, and literal values. This is the way to put JSONPath values into an array — a bare ref as a plain array element would serialize to a literal string, since ASL only substitutes paths in object keys ending in `.$`.
@example```ts statesArray(ctx.a, 'literal', ctx.b) // → "States.Array($.a, 'literal', $.b)" ```
statesArray
(
const ctx: Proxied<{
    a: string;
    b: string;
}>
ctx
.a: Ref<string>a, 'literal',
const ctx: Proxied<{
    a: string;
    b: string;
}>
ctx
.b: Ref<string>b);
const arr: IntrinsicExpr<string[]>

States.Array($.a, 'literal', $.b) — refs and literals mix, and the result is typed as an array of the element type.

The rest of the library

Every function follows the same shape: typed refs, intrinsics or literals in; IntrinsicExpr<Result> out.

type 
type Ctx = {
    frames: number[];
    key: string;
    defaults: {
        width: number;
    };
    overrides: {
        width: number;
    };
    count: number;
}
Ctx
= {
frames: number[]frames: number[]; key: stringkey: string;
defaults: {
    width: number;
}
defaults
: { width: numberwidth: number };
overrides: {
    width: number;
}
overrides
: { width: numberwidth: number };
count: numbercount: number; }; const const ctx: Proxied<Ctx>ctx = createProxy<Ctx>(path?: string[]): Proxied<Ctx>
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 Ctx = {
    frames: number[];
    key: string;
    defaults: {
        width: number;
    };
    overrides: {
        width: number;
    };
    count: number;
}
Ctx
>();
function statesArrayLength(ref: Ref<readonly unknown[]> | IntrinsicExpr<unknown[]>): IntrinsicExpr<number>
Step Functions `States.ArrayLength()` intrinsic function.
@example```ts statesArrayLength(ctx.scenes) // → "States.ArrayLength($.scenes)" ```
statesArrayLength
(const ctx: Proxied<Ctx>ctx.frames: Proxied<number[]>frames); // States.ArrayLength($.frames)
statesArrayGetItem<number>(array: ArrayArg<number>, index: NumberArg): IntrinsicExpr<number>
Step Functions `States.ArrayGetItem()` intrinsic function.
@example```ts statesArrayGetItem(ctx.scenes, ctx.index) // → "States.ArrayGetItem($.scenes, $.index)" ```
statesArrayGetItem
(const ctx: Proxied<Ctx>ctx.frames: Proxied<number[]>frames, 0); // States.ArrayGetItem($.frames, 0)
statesArrayPartition<number>(array: ArrayArg<number>, size: NumberArg): IntrinsicExpr<number[][]>
Step Functions `States.ArrayPartition()` intrinsic function. Chunks an array into sub-arrays of at most `size` elements.
@example```ts statesArrayPartition(ctx.frames, 100) // → "States.ArrayPartition($.frames, 100)" ```
statesArrayPartition
(const ctx: Proxied<Ctx>ctx.frames: Proxied<number[]>frames, 100); // States.ArrayPartition($.frames, 100)
function statesArrayRange(start: NumberArg, end: NumberArg, step: NumberArg): IntrinsicExpr<number[]>
Step Functions `States.ArrayRange()` intrinsic function. Produces `[start, start+step, …]` up to and including `end`. AWS caps the result at 1000 elements.
@example```ts statesArrayRange(0, ctx.frameCount, 10) // → "States.ArrayRange(0, $.frameCount, 10)" ```
statesArrayRange
(0, const ctx: Proxied<Ctx>ctx.count: Ref<number>count, 10); // States.ArrayRange(0, $.count, 10)
function statesStringSplit(value: StringArg, delimiter: StringArg): IntrinsicExpr<string[]>
Step Functions `States.StringSplit()` intrinsic function.
@example```ts statesStringSplit(ctx.key, '/') // → "States.StringSplit($.key, '/')" ```
statesStringSplit
(const ctx: Proxied<Ctx>ctx.key: Ref<string>key, '/'); // States.StringSplit($.key, '/')
function statesHash(data: StringArg | Ref<unknown> | IntrinsicExpr<unknown>, algorithm: HashAlgorithm): IntrinsicExpr<string>
Step Functions `States.Hash()` intrinsic function.
@example```ts statesHash(ctx.fileUpload.id, 'SHA-256') // → "States.Hash($.fileUpload.id, 'SHA-256')" ```
statesHash
(const ctx: Proxied<Ctx>ctx.key: Ref<string>key, 'SHA-256'); // States.Hash($.key, 'SHA-256')
function statesUuid(): IntrinsicExpr<string>
Step Functions `States.UUID()` intrinsic function. Generates a v4 UUID at execution time.
statesUuid
(); // States.UUID()
statesJsonMerge(const ctx: Proxied<Ctx>ctx.
defaults: Proxied<{
    width: number;
}>
defaults
, const ctx: Proxied<Ctx>ctx.
overrides: Proxied<{
    width: number;
}>
overrides
);
// States.JsonMerge($.defaults, $.overrides, false) — shallow only

statesJsonMerge is worth a note: ASL's merge is shallow, and the trailing false is the deep-merge flag that Step Functions does not support. Its result type is Omit<A, keyof B> & B, matching what a shallow merge actually produces.

In serialization

Intrinsics become .$ keys alongside refs and statics, with no special handling on your side:

const 
const ctx: Proxied<{
    id: string;
    data: unknown;
    count: number;
}>
ctx
=
createProxy<{
    id: string;
    data: unknown;
    count: number;
}>(path?: string[]): Proxied<{
    id: string;
    data: unknown;
    count: number;
}>
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
<{ id: stringid: string; data: unknowndata: unknown; count: numbercount: number }>();
function serializeParameters(obj: Record<string, unknown>): Record<string, unknown>
Recursively serialize a parameters object for ASL. - Ref values → `"key.$": "$.path"` - IntrinsicExpr values → `"key.$": "States.Format(...)"` - Nested objects → recursed - Arrays → each element recursed if it's an object - Primitives → kept as-is
serializeParameters
({
label: IntrinsicExpr<string>label: function statesFormat(template: string, ...args: (Ref<unknown> | IntrinsicExpr<unknown>)[]): IntrinsicExpr<string>
Step Functions `States.Format()` intrinsic function. Produces a string by interpolating `{}` placeholders in the template with the provided arguments (refs or other intrinsics). Single quotes in the template are escaped automatically. To include a literal `{` or `}`, escape it yourself as `\\{` / `\\}` per the ASL spec.
@example```ts statesFormat('scene_{}/frame', item.value.id) // → "States.Format('scene_{}/frame', $$.Map.Item.Value.id)" ```
statesFormat
('item_{}',
const ctx: Proxied<{
    id: string;
    data: unknown;
    count: number;
}>
ctx
.id: Ref<string>id),
payload: IntrinsicExpr<string>payload: function statesJsonToString(ref: Ref<unknown> | IntrinsicExpr<unknown>): IntrinsicExpr<string>
Step Functions `States.JsonToString()` intrinsic function. Converts a JSON value to its string representation.
@example```ts statesJsonToString(ctx.extractScenes) // → "States.JsonToString($.extractScenes)" ```
statesJsonToString
(
const ctx: Proxied<{
    id: string;
    data: unknown;
    count: number;
}>
ctx
.data: Ref<unknown>data),
nextCount: IntrinsicExpr<number>nextCount: function statesMathAdd(ref: Ref<number> | IntrinsicExpr<number>, operand: number): IntrinsicExpr<number>
Step Functions `States.MathAdd()` intrinsic function. Adds an integer operand to a numeric value referenced by a JSONPath.
@example```ts statesMathAdd(ctx.scene.start_frame, 1) // → "States.MathAdd($.scene.start_frame, 1)" ```
statesMathAdd
(
const ctx: Proxied<{
    id: string;
    data: unknown;
    count: number;
}>
ctx
.count: Ref<number>count, 1),
rawId: Ref<string>rawId:
const ctx: Proxied<{
    id: string;
    data: unknown;
    count: number;
}>
ctx
.id: Ref<string>id,
version: numberversion: 2, });
{
  "label.$": "States.Format('item_{}', $.id)",
  "payload.$": "States.JsonToString($.data)",
  "nextCount.$": "States.MathAdd($.count, 1)",
  "rawId.$": "$.id",
  "version": 2
}