-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.ts
156 lines (150 loc) · 4.7 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import { z } from "zod";
type IsPrimitive<T> = T extends object ? false : true;
export type ValidatedConstructor<
Schema extends z.ZodType<unknown>,
WrapValue extends boolean,
> = {
new (
value: z.input<Schema>,
): Readonly<
WrapValue extends true
? { value: Readonly<z.infer<Schema>> }
: z.infer<Schema>
>;
schema: Schema;
z: <T extends ValidatedConstructor<Schema, WrapValue>>(
this: T,
) => z.ZodType<InstanceType<T>, z.ZodTypeDef, z.input<Schema>>;
};
export type ValidatedMutableConstructor<
Schema extends z.ZodType<unknown>,
WrapValue extends boolean,
> = {
new (
value: z.input<Schema>,
): WrapValue extends true ? { value: z.infer<Schema> } : z.infer<Schema>;
schema: Schema;
z: <T extends ValidatedMutableConstructor<Schema, WrapValue>>(
this: T,
) => z.ZodType<InstanceType<T>, z.ZodTypeDef, z.input<Schema>>;
};
export const Validated = <
Schema extends z.ZodType<unknown>,
Options extends { wrapValue: true } | null = null,
>(
schema: Schema,
options?: Options,
) => {
const ctor = function Validated(
this: Record<string, unknown>,
value: z.input<typeof schema>,
) {
const validatedValue = schema.parse(value);
const wrapValue = !isObject(validatedValue) || options?.wrapValue;
const _this = wrapValue ? { value: validatedValue } : validatedValue;
return Object.create(this, Object.getOwnPropertyDescriptors(_this));
} as unknown as ValidatedConstructor<
Schema,
Options extends { wrapValue: true } ? true : IsPrimitive<z.infer<Schema>>
>;
ctor.schema = schema;
ctor.z = function <T extends typeof ctor>(this: T) {
return z.ZodAny.create().transform((data, ctx) => {
try {
return new this(data) as InstanceType<T>;
} catch (error) {
if (error instanceof z.ZodError) {
for (const issue of error.issues) {
ctx.addIssue(issue);
}
return z.NEVER;
}
throw error;
}
});
};
return ctor;
};
export const ValidatedMutable = <
Schema extends z.ZodType<unknown>,
Options extends { wrapValue: true } | null = null,
>(
schema: Schema,
options?: Options,
) => {
const makeValidatedValueProxy = (initialInput: unknown) => {
const inputObject: Record<string | symbol, unknown> = {};
if (isObject(initialInput)) {
Object.assign(inputObject, initialInput);
}
return (validatedValue: object) => {
return new Proxy(validatedValue, {
set(object, propertyName, newValue) {
inputObject[propertyName] = newValue;
const validatedNewValue = schema.parse(inputObject) as Record<
string | symbol,
unknown
>;
return Reflect.set(
object,
propertyName,
validatedNewValue[propertyName],
);
},
});
};
};
const ctor = function ValidatedMutable(
this: Record<string, unknown>,
value: z.input<typeof schema>,
) {
const validatedValue = schema.parse(value);
if (!isObject(validatedValue) || options?.wrapValue) {
const validatedValueProxy = isObject(validatedValue)
? makeValidatedValueProxy(value)(validatedValue)
: validatedValue;
const _this = { value: validatedValueProxy };
return new Proxy(
Object.create(this, Object.getOwnPropertyDescriptors(_this)),
{
set(object, propertyName, newValue) {
if (propertyName !== "value") {
return Reflect.set(object, propertyName, newValue);
}
const validatedNewValue = schema.parse(newValue);
const validatedNewValueProxy = isObject(validatedNewValue)
? makeValidatedValueProxy(newValue)(validatedNewValue)
: validatedNewValue;
return Reflect.set(object, "value", validatedNewValueProxy);
},
},
);
}
const _this = validatedValue;
return makeValidatedValueProxy(value)(
Object.create(this, Object.getOwnPropertyDescriptors(_this)),
);
} as unknown as ValidatedMutableConstructor<
Schema,
Options extends { wrapValue: true } ? true : IsPrimitive<z.infer<Schema>>
>;
ctor.schema = schema;
ctor.z = function <T extends typeof ctor>(this: T) {
return z.ZodAny.create().transform((data, ctx) => {
try {
return new this(data) as InstanceType<T>;
} catch (error) {
if (error instanceof z.ZodError) {
for (const issue of error.issues) {
ctx.addIssue(issue);
}
return z.NEVER;
}
throw error;
}
});
};
return ctor;
};
const isObject = (value: unknown): value is object =>
value !== null && (typeof value === "object" || typeof value === "function");