1.9 KiB
1.9 KiB
routes/shared/strip-undefined — drop undefined-valued keys
Purpose
Bridges Zod's optional-field semantics
(.optional() → string | undefined) with the suaveplan
@suaveplan/contacts input shapes, which use
exactOptionalPropertyTypes: true and therefore forbid the
| undefined half of optional members. Returning a shallow copy with
undefined keys removed lets the route handlers feed Zod output
directly into repo.create({ ...stripUndefined(parsed.data), tenantId })
without losing type safety.
Without this helper every route handler would need to manually
re-build the input object key-by-key, or every Zod schema would have
to redefine its optionality via .transform(). The helper is the
focused fix.
Features
- Shallow — preserves nested undefineds because the consumer Zod schemas only produce top-level optionals.
- Preserves
null,0,"", andfalse(onlyundefinedis dropped). - Pure / type-safe — returns
Partial<T>-compatible shape.
Usage: Basic
import { stripUndefined } from "@suaveplan/api-plugin-contacts";
const parsed = CreateContactSchema.parse(input);
const repoInput = stripUndefined(parsed);
await repo.create({ ...repoInput, tenantId });
Usage: Advanced
import { stripUndefined } from "@suaveplan/api-plugin-contacts";
function safeMerge<T extends Record<string, unknown>>(base: T, patch: Partial<T>): T {
return { ...base, ...stripUndefined(patch) } as T;
}
API Reference
stripUndefined<T>(input: T): { [K in keyof T]?: T[K] }— shallow copy withundefinedkeys omitted.
Implementation Notes
- Uses
Object.entriesso symbol-keyed properties are ignored; route inputs are always plain objects so this is fine. - Does not deep-clone — the returned object shares nested references with the input.
- The return type widens to
Partial<T>for ergonomics; the runtime contract is stricter (any non-undefinedoriginal value flows through unchanged).