Patterns
How do I accept an options object where every field is optional, but the empty object is not allowed?
Build one variant per key where that key is required and the rest are optional, then union them. Any single supplied key satisfies at least one variant.
The recipe
type AtLeastOne<T> = { [K in keyof T]: Required<Pick<T, K>> & Partial<Omit<T, K>> }[keyof T] type Filters = { name?: string age?: number } type ValidFilters = AtLeastOne<Filters>
The build compiles this and checks each result below.
How it works
- 01
Required<Pick<T, K>> & Partial<Omit<T, K>>
One variant: this key required, everything else optional. `Required` matters because the source properties were already optional.
- 02
}[keyof T]Index the whole table by every key at once, collapsing the variants into a union. The same trick the archive uses to turn a lookup table into an answer.
What you get
{ name: 'a' } extends ValidFilters ? true : false→true{} extends ValidFilters ? true : false→falseThe empty object satisfies no variant, because every variant requires exactly one key.
{ name: 'a'; age: 1 } extends ValidFilters ? true : false→true
Where it goes wrong
The union grows with the number of keys, and error messages grow with it — a ten-key options type produces a ten-member union and a diagnostic nobody will read. Keep it for small option bags.
Takeaway
Map to variants, then index by `keyof` to union them. That two-step is the general shape for "one of these, at least".