-
Notifications
You must be signed in to change notification settings - Fork 538
Open
Description
Problem
JavaScript has no expression-oriented blocks. When computing a value through conditionals or multiple steps, you're stuck with either mutable let bindings or IIFEs:
// Mutable — result is `let` for the rest of the scope
let result: string;
if (user.role === "admin") {
result = formatPerms(await fetchAdminPerms(user.id));
} else {
result = "none";
}
// IIFE — noisy, hard to scan
const result = (async () => {
if (user.role === "admin") return formatPerms(await fetchAdminPerms(user.id));
return "none";
})();IIFEs are widely considered a code smell in modern JavaScript:
eslint-plugin-no-iifebans them outright, calling IIFEs "a messy remnant of early Javascript" that "only serves to decrease readability"unicorn/consistent-function-scopingconflicts with IIFEs- Airbnb style guide: "in a world with modules everywhere, you almost never need an IIFE"
Examples in other languages
| Language | Feature | Example |
|---|---|---|
| Kotlin | run { } |
val x = run { ... } |
| Rust | Block expressions | let x = { ... }; |
| Scala | Block expressions | val x = { ... } |
| Ruby | Inline blocks | x = begin ... end |
Proposal
// Sync
const result = call(() => {
if (user.role === "admin") return "full";
if (user.role === "editor") return "partial";
return "none";
});
// Async
const result = await callAsync(async () => {
const perms = await fetchPerms(user.id);
const roles = await fetchRoles(user.id);
return merge(perms, roles);
});Implementation:
export function call<R>(fn: () => R): R {
return fn();
}
export async function callAsync<R>(fn: () => Promise<R>): Promise<R> {
return await fn();
}The implementation is trivial — but so are noop, identity, and attempt. The value is naming the pattern, not the complexity of the code. call(() => ...) communicates intent where (() => ...)() hides it in punctuation.
Precedent
es-toolkit already wraps simple patterns into named utilities:
attempt(fn)— wrapstry/catchnoop()— wraps() => {}identity(x)— wraps(x) => x
call(fn) fits the same category.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels