first commit
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# `strip-undefined`
|
||||
|
||||
## Purpose
|
||||
|
||||
`stripUndefined` is a route-layer adapter that bridges Zod's
|
||||
`optional()` output (`T | undefined`) with the
|
||||
`exactOptionalPropertyTypes: true` repository input types declared in
|
||||
`@suaveplan/campaigns` and the local `CampaignSuppressionStore`. When a
|
||||
route handler validates a request body through a Zod schema, optional
|
||||
fields surface as either the declared type or `undefined`; passing the
|
||||
parsed object straight to the repository fails the `exactOptional`
|
||||
check at compile time. This helper produces a shallow clone with every
|
||||
`undefined`-valued key removed so the downstream type narrows.
|
||||
|
||||
The helper is intentionally minimal — no recursion, no `null` handling,
|
||||
no key-name filtering — because every concrete usage in the routes
|
||||
layer passes a Zod-parsed object whose nesting has already been
|
||||
normalised. Recursion would obscure the cost model and tempt callers
|
||||
to use it as a deep-clean utility, which is out of scope.
|
||||
|
||||
## Features
|
||||
|
||||
- Shallow-only filter; nested objects are forwarded by reference.
|
||||
- `null` values are preserved (only `=== undefined` triggers removal).
|
||||
- Returns a fresh object reference every call (never mutates input).
|
||||
- Generic-typed return narrows `T[K]` to `Exclude<T[K], undefined>`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { stripUndefined } from "@suaveplan/api-plugin-campaigns";
|
||||
|
||||
const body = { name: "x", templateId: undefined } as const;
|
||||
const clean = stripUndefined(body);
|
||||
// clean: { name: "x" } — `templateId` removed.
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { stripUndefined } from "@suaveplan/api-plugin-campaigns";
|
||||
|
||||
const parsed = CreateCampaignSchema.parse(input);
|
||||
await repo.create({
|
||||
id: idGen(),
|
||||
tenantId,
|
||||
...stripUndefined(parsed),
|
||||
status: "draft",
|
||||
stats: EMPTY_STATS,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `stripUndefined<T extends Record<string, unknown>>(input: T):
|
||||
{ [K in keyof T]: Exclude<T[K], undefined> }` — shallow copy with
|
||||
`undefined`-valued keys removed.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The implementation iterates `Object.entries(input)` once and writes
|
||||
into a fresh `Record<string, unknown>` accumulator. The output is then
|
||||
cast to the narrowed mapped type — a single assertion confined to the
|
||||
return statement. The accumulator is not pre-sized because the helper
|
||||
is hot-path-cheap (single object scan) and JavaScript engines do not
|
||||
benefit from `new Object()` capacity hints. The forbidden `Object.assign`
|
||||
path (which would forward `undefined` properties when iterating
|
||||
`Object.keys`) is intentionally avoided.
|
||||
Reference in New Issue
Block a user