Files
suaveplan/packages/commerce/contacts/api-plugin-contacts/src/routes/shared/strip-undefined.md
2026-05-27 23:29:53 +01:00

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, "", and false (only undefined is 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 with undefined keys omitted.

Implementation Notes

  • Uses Object.entries so 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-undefined original value flows through unchanged).