Code Snippets
/

RequireAtLeastOne Utility Type

RequireAtLeastOne Utility Type

API endpoints often want 'pass at least one of these search params, but not necessarily all of them' (`{ id }`, `{ email }`, `{ id, email }`, but never `{}`). TypeScript's stock utilities cannot express that constraint, but a small `RequireAtLeastOne<T>` does. This snippet builds the type, layers in a `RequireExactlyOne` variant for either-or constraints, and shows a runtime guard that pairs with both.

TypeScript
Medium
3 snippets
ts-utility-types
ts-mapped-types
ts-conditional-types
ts-generics

456 views

2

type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>>
    & { [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];

interface SearchInput {
    id?: string;
    email?: string;
    handle?: string;
}

type Search = RequireAtLeastOne<SearchInput>;

const byId = { 'id': 'u1' } as Search;
const byEmail = { 'email': '[email protected]' } as Search;
console.log(byId.id, byEmail.email);

The type expands into a union: for each candidate key K, produce a shape where K is required and the other keys are optional, then union all of those shapes together. Pick<T, Exclude<keyof T, Keys>> keeps any non-candidate keys intact (useful when only a subset of T's keys participate in the constraint). The runtime sentinel only fills one key at a time, exactly the contract you want at the API boundary. Without this constraint, Partial<SearchInput> would silently allow {}, which usually means a buggy caller hitting the database with no filter.