Skip to content

Commit 278241f

Browse files
committed
Moving objectMapper as remap() into the dedicated file.
1 parent 127ea2c commit 278241f

2 files changed

Lines changed: 28 additions & 25 deletions

File tree

zod-plugin/remap.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
/**
2-
* @fileoverview Mapping utils for Zod Runtime Plugin (remap)
3-
* @link https://stackoverflow.com/questions/55454125/typescript-remapping-object-properties-in-typesafe
4-
* @todo try to reuse R.Remap from Ramda (requires to move its types to prod dependencies)
5-
*/
1+
import { z } from "zod";
2+
import * as R from "ramda";
3+
64
type TuplesFromObject<T> = {
75
[P in keyof T]: [P, T[P]];
86
}[keyof T];
@@ -14,8 +12,31 @@ type GetKeyByValue<T, V> =
1412
: never
1513
: never;
1614

15+
/**
16+
* @fileoverview Mapping utils for Zod Runtime Plugin (remap)
17+
* @link https://stackoverflow.com/questions/55454125/typescript-remapping-object-properties-in-typesafe
18+
* @todo try to reuse R.Remap from Ramda (requires to move its types to prod dependencies)
19+
*/
1720
export type Remap<T, U extends { [P in keyof T]?: V }, V extends string> = {
1821
[P in NonNullable<U[keyof U]>]: T[GetKeyByValue<U, P>];
1922
};
2023

2124
export type Intact<T, U> = { [K in Exclude<keyof T, keyof U>]: T[K] };
25+
26+
type Mapper = <T extends Record<string, unknown>>(
27+
subject: T,
28+
) => { [P in string | keyof T]: T[keyof T] };
29+
30+
export const remap = function (
31+
this: z.ZodObject,
32+
tool: Record<string, string> | Mapper,
33+
) {
34+
const transformer =
35+
typeof tool === "function" ? tool : R.renameKeys(R.reject(R.isNil, tool)); // rejecting undefined
36+
const nextShape = transformer(
37+
R.map(R.invoker(0, "clone"), this._zod.def.shape), // immutable, changed from R.clone due to failure
38+
);
39+
const hasPassThrough = this._zod.def.catchall instanceof z.ZodUnknown;
40+
const output = (hasPassThrough ? z.looseObject : z.object)(nextShape); // proxies unknown keys when set to "passthrough"
41+
return this.transform(transformer).pipe(output);
42+
};

zod-plugin/runtime.ts

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import * as R from "ramda";
21
import { globalRegistry, z } from "zod";
32
import { name } from "./package.json";
43
import { setBrand } from "./brand";
4+
import { remap } from "./remap";
55

66
const exampleSetter = function (this: z.ZodType, value: z.output<typeof this>) {
77
const examples = globalRegistry.get(this)?.examples?.slice() || [];
@@ -17,24 +17,6 @@ const labelSetter = function (this: z.ZodDefault, defaultLabel: string) {
1717
return this.meta({ default: defaultLabel });
1818
};
1919

20-
type _Mapper = <T extends Record<string, unknown>>(
21-
subject: T,
22-
) => { [P in string | keyof T]: T[keyof T] };
23-
24-
const objectMapper = function (
25-
this: z.ZodObject,
26-
tool: Record<string, string> | _Mapper,
27-
) {
28-
const transformer =
29-
typeof tool === "function" ? tool : R.renameKeys(R.reject(R.isNil, tool)); // rejecting undefined
30-
const nextShape = transformer(
31-
R.map(R.invoker(0, "clone"), this._zod.def.shape), // immutable, changed from R.clone due to failure
32-
);
33-
const hasPassThrough = this._zod.def.catchall instanceof z.ZodUnknown;
34-
const output = (hasPassThrough ? z.looseObject : z.object)(nextShape); // proxies unknown keys when set to "passthrough"
35-
return this.transform(transformer).pipe(output);
36-
};
37-
3820
const pluginFlag = Symbol.for(name);
3921

4022
if (!(pluginFlag in globalThis)) {
@@ -70,6 +52,6 @@ if (!(pluginFlag in globalThis)) {
7052
Object.defineProperty(
7153
z.ZodObject.prototype,
7254
"remap" satisfies keyof z.ZodObject,
73-
{ value: objectMapper, writable: false },
55+
{ value: remap, writable: false },
7456
);
7557
}

0 commit comments

Comments
 (0)