Expressions over mutation
Produce one value from branching control flow with an immediately invoked callback, and know when the block deserves a name instead.
Last updated
After reading this guide, you will know
- How
iifeturnsifchains into a single assignment - What
iifereturns for a sync and an async callback - When to extract a named function instead
A value decided by more than one condition tends to arrive as an outer let filled by
if/else, or as a nested ternary. @sdxc/iife runs a callback on the spot and
hands back what it returns, so a block of statements sits where only an expression fits and the
value is assigned exactly once.
npm add @sdxc/iifeAssign once with an immediately invoked callback
Wrap the decision in iife and each branch returns its value instead of assigning an outer
let:
import { iife } from "@sdxc/iife";
export interface Draft {
title: string;
publishedAt: Date | null;
scheduledFor: Date | null;
}
export function describeDraft(draft: Draft) {
let label = iife(() => {
if (draft.publishedAt !== null) return "Published";
if (draft.scheduledFor !== null) return "Scheduled";
return "Draft";
});
return `${draft.title} (${label})`;
}iife calls the callback once, synchronously, and returns its result, so label takes the type
of what the branches return. The same block fits an object field or a JSX child, where a nested
ternary usually appears.
An async callback gives you a promise, which you await like any other:
let viewer = await iife(async () => { … }). A callback that throws surfaces where the iife
call stands; for code that throws, reach for wrap from
@sdxc/result, which
returns the failure as a value.
Extract a named function when the block grows
iife suits a decision short enough to read in place. Give the block a name when another
module needs the same decision, when it deserves a test of its own, or when it reads many
locals from the enclosing scope:
export interface PlanState {
plan: "free" | "pro" | "enterprise";
trialEndsAt: Date | null;
}
/** The support line every page shows for a plan; an active trial outranks the plan's own. */
export function planNotice({ plan, trialEndsAt }: PlanState): string {
if (plan === "enterprise") return "Priority support is on.";
if (trialEndsAt !== null) return `Trial ends ${trialEndsAt.toDateString()}.`;
return "Standard support.";
}Test the decision through its function
An inline iife is part of the function around it, so test that function: every branch of the
callback is a case of its output.
import { expect, test } from "vitest";
import { describeDraft } from "~/app/services/drafts/status";
test("labels a draft by the furthest state it reached", () => {
let base = { title: "Launch", publishedAt: null, scheduledFor: null };
expect(describeDraft(base)).toBe("Launch (Draft)");
expect(describeDraft({ ...base, scheduledFor: new Date() })).toBe(
"Launch (Scheduled)",
);
expect(describeDraft({ ...base, publishedAt: new Date() })).toBe(
"Launch (Published)",
);
});Where to go next
Result everywhere —
wrapfor code that throws, and the early returns these callbacks are written in.Naming — the names an extracted function and its exports follow.
@sdxc/iife— the one export and its signature.