unless()
unless<
T,Result>(value,predicate,transformation):T|Result
Applies a transformation to a value when a predicate is false, otherwise returns the original value.
This is the logical opposite of when.
💎 Why is this a Hidden Gem?
The logical inverse of when. Applies a transformation only if the predicate is false.
Logical opposite of when.
Type Parameters
T: T
The type of the input value.
Result: Result
The type of the transformed value.
Parameters
value: T
The value to potentially transform.
predicate: (value) => boolean
A function that determines when NOT to apply the transformation.
transformation: (value) => Result
The function to apply when the predicate is false.
Returns: T | Result
The transformed value if predicate is false, otherwise the original value.
See Also
Since
2.0.0
Also known as
unless (Ramda) · ❌ (Lodash, es-toolkit, Remeda, Radashi, Effect, Modern Dash, Antfu)
Example
unless(4, (n) => n % 2 !== 0, (n) => n * 2);
// => 8 (4 is not odd, so transformation applies)
unless(3, (n) => n % 2 !== 0, (n) => n * 2);
// => 3 (3 is odd, so no transformation)
unless('hello', (s) => s.trim() === '', (s) => s.toUpperCase());
// => 'HELLO' (not empty, so transformation applies)
unless('', (s) => s.trim() === '', (s) => s.toUpperCase());
// => '' (empty, so no transformation)
How it works?
Applies a transformation only when the predicate returns false.
If predicate is true, returns the original value unchanged. Logical opposite of when.
Predicate False (transforms)
Predicate True (skips)
unless vs when
| Input | when(x, isOdd, double) | unless(x, isOdd, double) |
|---|---|---|
| 3 (odd) | transforms → 6 | skips → 3 |
| 4 (even) | skips → 4 | transforms → 8 |
Use Cases
Add defaults only when missing 📌
Add 'https://' prefix only when URL doesn't already have it. Essential for URL normalization and ensuring valid links.
const ensureProto = (url) =>
unless(url, (u) => u.startsWith("http"), (u) => `https://${u}`);
ensureProto("google.com"); // "https://google.com"
ensureProto("https://site.com"); // "https://site.com"
Skip validation for admin users
Apply validation rules only for non-admin users. Critical for permission systems and role-based logic.
const validate = (user) =>
unless(user, (u) => u.isAdmin, (u) => {
if (!u.email) throw new Error("Email required");
return u;
});
Lazy-load images unless already in viewport
Skip lazy-loading for images already visible on initial render. Essential for above-the-fold optimization and Core Web Vitals.
const maybeLazyLoad = (img: HTMLImageElement) =>
unless(img, (el) => el.getBoundingClientRect().top < window.innerHeight, (el) => {
el.loading = "lazy";
return el;
});
document.querySelectorAll("img").forEach(maybeLazyLoad);
Skip overlay backdrop on mobile
Skip rendering the backdrop overlay on mobile where it causes scroll issues. Perfect for responsive overlay components that behave differently on mobile.
const overlayConfig = (config: OverlayConfig) =>
unless(config, () => window.innerWidth >= 768, (c) => ({
...c,
hasBackdrop: false,
fullScreen: true,
}));
// Desktop: keeps backdrop, normal overlay
// Mobile: no backdrop, full screen sheet
Show tooltip unless touch device
Disable hover tooltips on touch devices where they cause UX issues. Perfect for design systems supporting both desktop and mobile.
const maybeTooltip = (content: string) =>
unless(content, () => "ontouchstart" in window, (text) => ({
tooltip: text,
showOnHover: true,
}));
maybeTooltip("Click to edit"); // Desktop: { tooltip: "Click to edit", showOnHover: true }
// Touch device: "Click to edit" (no tooltip wrapper)
transforms → 6
skips → 3