Skip to main content

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.

note

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​

when


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​

Inputwhen(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)