@zod-utils/react-hook-form 0.1.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -96
- package/dist/index.d.mts +177 -49
- package/dist/index.d.ts +177 -49
- package/dist/index.js +1 -281
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -278
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -5
package/README.md
CHANGED
|
@@ -1,34 +1,79 @@
|
|
|
1
1
|
# @zod-utils/react-hook-form
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
[](https://www.npmjs.com/package/@zod-utils/react-hook-form)
|
|
5
4
|
[](https://www.npmjs.com/package/@zod-utils/react-hook-form)
|
|
5
|
+
[](https://bundlephobia.com/package/@zod-utils/react-hook-form)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://www.typescriptlang.org/)
|
|
8
8
|
[](https://github.com/thu-san/zod-utils/actions)
|
|
9
|
+
[](https://codecov.io/gh/thu-san/zod-utils)
|
|
10
|
+
|
|
9
11
|
React Hook Form integration and utilities for Zod schemas.
|
|
10
12
|
|
|
13
|
+
## 💡 Why Use This?
|
|
14
|
+
|
|
15
|
+
**The whole point:** Automatically transforms your Zod schema types so form inputs accept `undefined` (and `null` for objects only) during editing, while the validated output remains exactly as your Zod schema defines.
|
|
16
|
+
|
|
17
|
+
No more type wrestling with React Hook Form - just pass your schema and it works.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { useZodForm } from "@zod-utils/react-hook-form";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
|
|
23
|
+
// Your schema with primitives, arrays, and objects - NOT optional
|
|
24
|
+
const schema = z.object({
|
|
25
|
+
username: z.string().min(3),
|
|
26
|
+
age: z.number().min(18),
|
|
27
|
+
tags: z.array(z.string()),
|
|
28
|
+
profile: z.object({ bio: z.string() }),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const form = useZodForm({ schema });
|
|
32
|
+
|
|
33
|
+
// ✅ Works! Primitives and arrays accept undefined during editing
|
|
34
|
+
form.setValue("username", undefined);
|
|
35
|
+
form.setValue("age", undefined);
|
|
36
|
+
form.setValue("tags", undefined);
|
|
37
|
+
|
|
38
|
+
// ✅ Works! Objects accept both null and undefined
|
|
39
|
+
form.setValue("profile", null);
|
|
40
|
+
form.setValue("profile", undefined);
|
|
41
|
+
|
|
42
|
+
// ✅ Validated output type is exactly z.infer<typeof schema>
|
|
43
|
+
const onSubmit = form.handleSubmit((data) => {
|
|
44
|
+
// Type: { username: string; age: number; tags: string[]; profile: { bio: string } }
|
|
45
|
+
// NOT { username: string | null | undefined; ... }
|
|
46
|
+
console.log(data.username); // Type: string
|
|
47
|
+
console.log(data.age); // Type: number
|
|
48
|
+
console.log(data.tags); // Type: string[]
|
|
49
|
+
console.log(data.profile); // Type: { bio: string }
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
11
53
|
## Installation
|
|
12
54
|
|
|
13
55
|
```bash
|
|
14
56
|
npm install @zod-utils/react-hook-form zod react react-hook-form @hookform/resolvers
|
|
15
57
|
```
|
|
16
58
|
|
|
59
|
+
## Related Packages
|
|
60
|
+
|
|
61
|
+
- **[@zod-utils/core](https://www.npmjs.com/package/@zod-utils/core)** - Pure TypeScript utilities for Zod schema manipulation (no React dependencies). All utilities are re-exported from this package for convenience.
|
|
62
|
+
|
|
17
63
|
## Features
|
|
18
64
|
|
|
19
|
-
- 🎣 **useZodForm** -
|
|
20
|
-
- 🌐 **Custom error messages** - Japanese error resolver (customizable)
|
|
65
|
+
- 🎣 **useZodForm** - Automatic type transformation for form inputs (nullable/undefined) while preserving Zod schema validation
|
|
21
66
|
- 📦 **All core utilities** - Re-exports everything from `@zod-utils/core`
|
|
22
67
|
- ⚛️ **React-optimized** - Built specifically for React applications
|
|
23
68
|
|
|
24
69
|
## Quick Start
|
|
25
70
|
|
|
26
71
|
```typescript
|
|
27
|
-
import { useZodForm, getSchemaDefaults } from
|
|
28
|
-
import { z } from
|
|
72
|
+
import { useZodForm, getSchemaDefaults } from "@zod-utils/react-hook-form";
|
|
73
|
+
import { z } from "zod";
|
|
29
74
|
|
|
30
75
|
const schema = z.object({
|
|
31
|
-
name: z.string().default(
|
|
76
|
+
name: z.string().default("John Doe"),
|
|
32
77
|
email: z.string().email(),
|
|
33
78
|
age: z.number().min(18),
|
|
34
79
|
});
|
|
@@ -45,9 +90,9 @@ function MyForm() {
|
|
|
45
90
|
|
|
46
91
|
return (
|
|
47
92
|
<form onSubmit={onSubmit}>
|
|
48
|
-
<input {...form.register(
|
|
49
|
-
<input {...form.register(
|
|
50
|
-
<input {...form.register(
|
|
93
|
+
<input {...form.register("name")} />
|
|
94
|
+
<input {...form.register("email")} type="email" />
|
|
95
|
+
<input {...form.register("age")} type="number" />
|
|
51
96
|
<button type="submit">Submit</button>
|
|
52
97
|
</form>
|
|
53
98
|
);
|
|
@@ -61,8 +106,8 @@ function MyForm() {
|
|
|
61
106
|
Type-safe wrapper around React Hook Form's `useForm` with automatic Zod schema integration.
|
|
62
107
|
|
|
63
108
|
```typescript
|
|
64
|
-
import { useZodForm } from
|
|
65
|
-
import { z } from
|
|
109
|
+
import { useZodForm } from "@zod-utils/react-hook-form";
|
|
110
|
+
import { z } from "zod";
|
|
66
111
|
|
|
67
112
|
const schema = z.object({
|
|
68
113
|
username: z.string().min(3),
|
|
@@ -70,124 +115,164 @@ const schema = z.object({
|
|
|
70
115
|
});
|
|
71
116
|
|
|
72
117
|
const form = useZodForm({
|
|
73
|
-
schema,
|
|
74
|
-
defaultValues: {
|
|
75
|
-
|
|
118
|
+
schema, // Zod schema (required)
|
|
119
|
+
defaultValues: {
|
|
120
|
+
/* ... */
|
|
121
|
+
}, // Optional default values
|
|
122
|
+
zodResolverOptions: {
|
|
123
|
+
/* ... */
|
|
124
|
+
}, // Optional zodResolver options
|
|
76
125
|
// ... all other useForm options
|
|
77
126
|
});
|
|
78
127
|
```
|
|
79
128
|
|
|
80
|
-
**
|
|
81
|
-
- Zod schema validation via `zodResolver`
|
|
82
|
-
- Proper TypeScript typing
|
|
83
|
-
- Form state management
|
|
129
|
+
**What it does:**
|
|
84
130
|
|
|
85
|
-
|
|
131
|
+
- **Input transformation** (by default):
|
|
132
|
+
- **Primitive fields** (string, number, boolean) accept `undefined` only
|
|
133
|
+
- **Array fields** accept `undefined` only
|
|
134
|
+
- **Object fields** accept both `null` and `undefined`
|
|
135
|
+
- You can override this by specifying a custom input type (see examples below)
|
|
136
|
+
- **Output validation**: Validated data matches your Zod schema exactly
|
|
137
|
+
- **Type inference**: No manual type annotations needed - everything is inferred from the schema
|
|
138
|
+
- **Zod integration**: Automatically sets up `zodResolver` for validation
|
|
86
139
|
|
|
87
|
-
|
|
140
|
+
#### Custom Input Types
|
|
88
141
|
|
|
89
|
-
|
|
142
|
+
You can override the default input type transformation if needed:
|
|
90
143
|
|
|
91
144
|
```typescript
|
|
92
|
-
import {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
...FieldNamespaceMapping,
|
|
98
|
-
myForm: {
|
|
99
|
-
username: 'ユーザー名',
|
|
100
|
-
email: 'メールアドレス',
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const errorMap = customErrorResolver({
|
|
105
|
-
fieldNamespace: 'myForm',
|
|
106
|
-
});
|
|
145
|
+
import {
|
|
146
|
+
useZodForm,
|
|
147
|
+
PartialWithAllNullables,
|
|
148
|
+
} from "@zod-utils/react-hook-form";
|
|
149
|
+
import { z } from "zod";
|
|
107
150
|
|
|
108
151
|
const schema = z.object({
|
|
109
|
-
username: z.string(),
|
|
152
|
+
username: z.string().min(3),
|
|
110
153
|
email: z.string().email(),
|
|
154
|
+
age: z.number(),
|
|
111
155
|
});
|
|
112
156
|
|
|
113
|
-
// Use
|
|
114
|
-
|
|
115
|
-
|
|
157
|
+
// Option 1: Use PartialWithAllNullables to make ALL fields accept null
|
|
158
|
+
const form = useZodForm<
|
|
159
|
+
z.infer<typeof schema>,
|
|
160
|
+
PartialWithAllNullables<z.infer<typeof schema>>
|
|
161
|
+
>({
|
|
162
|
+
schema,
|
|
163
|
+
defaultValues: { username: null, email: null, age: null },
|
|
164
|
+
});
|
|
116
165
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
166
|
+
// Option 2: Specify exact input types per field
|
|
167
|
+
const form2 = useZodForm<
|
|
168
|
+
z.infer<typeof schema>,
|
|
169
|
+
{
|
|
170
|
+
username?: string | null; // Can be set to null
|
|
171
|
+
email?: string; // Can only be undefined
|
|
172
|
+
age?: number | null; // Can be set to null
|
|
173
|
+
}
|
|
174
|
+
>({
|
|
175
|
+
schema,
|
|
176
|
+
defaultValues: { username: null, email: undefined, age: null },
|
|
177
|
+
});
|
|
178
|
+
```
|
|
123
179
|
|
|
124
180
|
---
|
|
125
181
|
|
|
126
|
-
|
|
182
|
+
## Core Utilities (Re-exported)
|
|
127
183
|
|
|
128
|
-
|
|
184
|
+
All utilities from `@zod-utils/core` are re-exported for convenience:
|
|
129
185
|
|
|
130
186
|
```typescript
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
187
|
+
import {
|
|
188
|
+
// Schema utilities (from @zod-utils/core)
|
|
189
|
+
getSchemaDefaults,
|
|
190
|
+
requiresValidInput,
|
|
191
|
+
getPrimitiveType,
|
|
192
|
+
removeDefault,
|
|
193
|
+
extractDefault,
|
|
194
|
+
type Simplify,
|
|
137
195
|
|
|
138
|
-
|
|
196
|
+
// Type utilities (react-hook-form specific)
|
|
197
|
+
type PartialWithNullableObjects,
|
|
198
|
+
type PartialWithAllNullables,
|
|
199
|
+
} from "@zod-utils/react-hook-form";
|
|
139
200
|
```
|
|
140
201
|
|
|
141
|
-
|
|
202
|
+
See [@zod-utils/core documentation](../core/README.md) for details on schema utilities.
|
|
203
|
+
|
|
204
|
+
### Type Utilities
|
|
205
|
+
|
|
206
|
+
#### `PartialWithNullableObjects<T>`
|
|
207
|
+
|
|
208
|
+
Transforms properties based on their type. Primitive and array fields become optional-only (not nullable), while object fields become optional and nullable.
|
|
209
|
+
|
|
210
|
+
**Transformation rules:**
|
|
211
|
+
|
|
212
|
+
- **Primitives** (string, number, boolean): optional → `type | undefined`
|
|
213
|
+
- **Arrays**: optional → `type[] | undefined`
|
|
214
|
+
- **Objects**: optional and nullable → `type | null | undefined`
|
|
215
|
+
|
|
142
216
|
```typescript
|
|
143
|
-
import {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
},
|
|
217
|
+
import type { PartialWithNullableObjects } from "@zod-utils/react-hook-form";
|
|
218
|
+
|
|
219
|
+
type User = {
|
|
220
|
+
name: string;
|
|
221
|
+
age: number;
|
|
222
|
+
tags: string[];
|
|
223
|
+
profile: { bio: string };
|
|
151
224
|
};
|
|
225
|
+
|
|
226
|
+
type FormInput = PartialWithNullableObjects<User>;
|
|
227
|
+
// {
|
|
228
|
+
// name?: string; // Primitive: optional, not nullable
|
|
229
|
+
// age?: number; // Primitive: optional, not nullable
|
|
230
|
+
// tags?: string[]; // Array: optional, not nullable
|
|
231
|
+
// profile?: { bio: string } | null; // Object: optional AND nullable
|
|
232
|
+
// }
|
|
152
233
|
```
|
|
153
234
|
|
|
154
|
-
|
|
235
|
+
This type is used internally by `useZodForm` to allow form fields to accept undefined (and null for objects only) during editing while maintaining proper validation types.
|
|
155
236
|
|
|
156
|
-
|
|
237
|
+
#### `PartialWithAllNullables<T>`
|
|
157
238
|
|
|
158
|
-
|
|
239
|
+
Makes all fields optional and nullable, regardless of type.
|
|
240
|
+
|
|
241
|
+
**Transformation rules:**
|
|
242
|
+
|
|
243
|
+
- **All fields**: optional and nullable → `type | null | undefined`
|
|
159
244
|
|
|
160
245
|
```typescript
|
|
161
|
-
import {
|
|
162
|
-
// Schema utilities
|
|
163
|
-
getSchemaDefaults,
|
|
164
|
-
checkIfFieldIsRequired,
|
|
165
|
-
getPrimitiveType,
|
|
166
|
-
removeDefault,
|
|
167
|
-
extractDefault,
|
|
168
|
-
getUnwrappedType,
|
|
246
|
+
import type { PartialWithAllNullables } from "@zod-utils/react-hook-form";
|
|
169
247
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
248
|
+
type User = {
|
|
249
|
+
name: string;
|
|
250
|
+
age: number;
|
|
251
|
+
tags: string[];
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
type FormInput = PartialWithAllNullables<User>;
|
|
255
|
+
// {
|
|
256
|
+
// name?: string | null; // All fields: optional AND nullable
|
|
257
|
+
// age?: number | null; // All fields: optional AND nullable
|
|
258
|
+
// tags?: string[] | null; // All fields: optional AND nullable
|
|
259
|
+
// }
|
|
175
260
|
```
|
|
176
261
|
|
|
177
|
-
|
|
262
|
+
Use this when all fields need to accept `null`, not just objects/arrays.
|
|
178
263
|
|
|
179
264
|
---
|
|
180
265
|
|
|
181
266
|
## Complete Example
|
|
182
267
|
|
|
183
268
|
```typescript
|
|
184
|
-
import { useZodForm, getSchemaDefaults } from
|
|
185
|
-
import { z } from
|
|
269
|
+
import { useZodForm, getSchemaDefaults } from "@zod-utils/react-hook-form";
|
|
270
|
+
import { z } from "zod";
|
|
186
271
|
|
|
187
272
|
const userSchema = z.object({
|
|
188
273
|
profile: z.object({
|
|
189
|
-
firstName: z.string().min(1,
|
|
190
|
-
lastName: z.string().min(1,
|
|
274
|
+
firstName: z.string().min(1, "First name is required"),
|
|
275
|
+
lastName: z.string().min(1, "Last name is required"),
|
|
191
276
|
age: z.number().min(18).max(120),
|
|
192
277
|
}),
|
|
193
278
|
contact: z.object({
|
|
@@ -195,7 +280,7 @@ const userSchema = z.object({
|
|
|
195
280
|
phone: z.string().optional(),
|
|
196
281
|
}),
|
|
197
282
|
preferences: z.object({
|
|
198
|
-
theme: z.enum([
|
|
283
|
+
theme: z.enum(["light", "dark"]).default("light"),
|
|
199
284
|
notifications: z.boolean().default(true),
|
|
200
285
|
}),
|
|
201
286
|
});
|
|
@@ -207,44 +292,47 @@ function UserForm() {
|
|
|
207
292
|
});
|
|
208
293
|
|
|
209
294
|
const onSubmit = form.handleSubmit((data) => {
|
|
210
|
-
console.log(
|
|
295
|
+
console.log("Valid data:", data);
|
|
211
296
|
});
|
|
212
297
|
|
|
213
298
|
return (
|
|
214
299
|
<form onSubmit={onSubmit}>
|
|
215
300
|
{/* Profile */}
|
|
216
|
-
<input {...form.register(
|
|
301
|
+
<input {...form.register("profile.firstName")} />
|
|
217
302
|
{form.formState.errors.profile?.firstName && (
|
|
218
303
|
<span>{form.formState.errors.profile.firstName.message}</span>
|
|
219
304
|
)}
|
|
220
305
|
|
|
221
|
-
<input {...form.register(
|
|
306
|
+
<input {...form.register("profile.lastName")} />
|
|
222
307
|
{form.formState.errors.profile?.lastName && (
|
|
223
308
|
<span>{form.formState.errors.profile.lastName.message}</span>
|
|
224
309
|
)}
|
|
225
310
|
|
|
226
|
-
<input
|
|
311
|
+
<input
|
|
312
|
+
{...form.register("profile.age", { valueAsNumber: true })}
|
|
313
|
+
type="number"
|
|
314
|
+
/>
|
|
227
315
|
{form.formState.errors.profile?.age && (
|
|
228
316
|
<span>{form.formState.errors.profile.age.message}</span>
|
|
229
317
|
)}
|
|
230
318
|
|
|
231
319
|
{/* Contact */}
|
|
232
|
-
<input {...form.register(
|
|
320
|
+
<input {...form.register("contact.email")} type="email" />
|
|
233
321
|
{form.formState.errors.contact?.email && (
|
|
234
322
|
<span>{form.formState.errors.contact.email.message}</span>
|
|
235
323
|
)}
|
|
236
324
|
|
|
237
|
-
<input {...form.register(
|
|
325
|
+
<input {...form.register("contact.phone")} type="tel" />
|
|
238
326
|
|
|
239
327
|
{/* Preferences - pre-filled with defaults */}
|
|
240
|
-
<select {...form.register(
|
|
328
|
+
<select {...form.register("preferences.theme")}>
|
|
241
329
|
<option value="light">Light</option>
|
|
242
330
|
<option value="dark">Dark</option>
|
|
243
331
|
</select>
|
|
244
332
|
|
|
245
333
|
<label>
|
|
246
334
|
<input
|
|
247
|
-
{...form.register(
|
|
335
|
+
{...form.register("preferences.notifications")}
|
|
248
336
|
type="checkbox"
|
|
249
337
|
/>
|
|
250
338
|
Enable notifications
|
|
@@ -269,10 +357,10 @@ const form = useZodForm({
|
|
|
269
357
|
});
|
|
270
358
|
|
|
271
359
|
// ✅ Fully typed
|
|
272
|
-
form.register(
|
|
360
|
+
form.register("profile.firstName");
|
|
273
361
|
|
|
274
362
|
// ❌ TypeScript error
|
|
275
|
-
form.register(
|
|
363
|
+
form.register("nonexistent.field");
|
|
276
364
|
```
|
|
277
365
|
|
|
278
366
|
---
|
package/dist/index.d.mts
CHANGED
|
@@ -1,66 +1,194 @@
|
|
|
1
|
+
export * from '@zod-utils/core';
|
|
1
2
|
import * as react_hook_form from 'react-hook-form';
|
|
2
3
|
import { FieldValues, DefaultValues, UseFormProps } from 'react-hook-form';
|
|
3
4
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper type that adds `null` to object-type fields only (excludes arrays).
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
type AddNullToObjects<T> = {
|
|
12
|
+
[K in keyof T]: T[K] extends readonly unknown[] ? T[K] : T[K] extends object ? T[K] | null : T[K];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Transforms Zod schema types for form inputs.
|
|
16
|
+
*
|
|
17
|
+
* - **Primitives** (string, number, boolean): optional → `type | undefined`
|
|
18
|
+
* - **Arrays**: optional → `type[] | undefined`
|
|
19
|
+
* - **Objects**: optional and nullable → `type | null | undefined`
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* type User = { name: string; tags: string[]; profile: { bio: string } };
|
|
24
|
+
* type FormInput = PartialWithNullableObjects<User>;
|
|
25
|
+
* // { name?: string; tags?: string[]; profile?: { bio: string } | null; }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
type PartialWithNullableObjects<T> = Partial<AddNullToObjects<T>>;
|
|
29
|
+
/**
|
|
30
|
+
* Makes all fields optional and nullable.
|
|
31
|
+
*
|
|
32
|
+
* - **All fields**: optional and nullable → `type | null | undefined`
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* type User = { name: string; age: number; tags: string[] };
|
|
37
|
+
* type FormInput = PartialWithAllNullables<User>;
|
|
38
|
+
* // { name?: string | null; age?: number | null; tags?: string[] | null; }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
type PartialWithAllNullables<T> = {
|
|
42
|
+
[K in keyof T]?: T[K] | null;
|
|
43
|
+
};
|
|
7
44
|
|
|
8
45
|
/**
|
|
9
|
-
* Type-safe
|
|
10
|
-
*
|
|
46
|
+
* Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.
|
|
47
|
+
*
|
|
48
|
+
* This hook eliminates the TypeScript friction between React Hook Form's nullable field values
|
|
49
|
+
* and Zod's strict output types. It uses a two-type schema pattern where:
|
|
50
|
+
* - **Input type** (`PartialWithNullableObjects<T>`): Form fields accept `null | undefined` during editing
|
|
51
|
+
* - **Output type** (`T`): Validated data matches exact schema type (no `null | undefined`)
|
|
52
|
+
*
|
|
53
|
+
* **Key Benefits:**
|
|
54
|
+
* - ✅ No more "Type 'null' is not assignable to..." TypeScript errors
|
|
55
|
+
* - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely
|
|
56
|
+
* - ✅ Validated output is still type-safe with exact Zod schema types
|
|
57
|
+
* - ✅ Automatic zodResolver setup - no manual configuration needed
|
|
58
|
+
*
|
|
59
|
+
* @template T - The Zod schema output type (extends FieldValues)
|
|
60
|
+
*
|
|
61
|
+
* @param options - Configuration object
|
|
62
|
+
* @param options.schema - Zod schema with two-type signature `z.ZodType<T, PartialWithNullableObjects<T>>`
|
|
63
|
+
* @param options.defaultValues - Default form values (accepts nullable/undefined values)
|
|
64
|
+
* @param options.zodResolverOptions - Optional zodResolver configuration
|
|
65
|
+
* @param options....formOptions - All other react-hook-form useForm options
|
|
66
|
+
*
|
|
67
|
+
* @returns React Hook Form instance with type-safe methods
|
|
11
68
|
*
|
|
12
69
|
* @example
|
|
13
|
-
*
|
|
70
|
+
* Basic usage with required fields
|
|
71
|
+
* ```typescript
|
|
72
|
+
* import { useZodForm } from '@zod-utils/react-hook-form';
|
|
73
|
+
* import { z } from 'zod';
|
|
74
|
+
*
|
|
14
75
|
* const schema = z.object({
|
|
15
|
-
* name: z.string(),
|
|
16
|
-
* age: z.number()
|
|
76
|
+
* name: z.string().min(1), // Required field
|
|
77
|
+
* age: z.number().min(0),
|
|
78
|
+
* }) satisfies z.ZodType<{ name: string; age: number }, any>;
|
|
79
|
+
*
|
|
80
|
+
* function MyForm() {
|
|
81
|
+
* const form = useZodForm({ schema });
|
|
82
|
+
*
|
|
83
|
+
* // ✅ These work without type errors:
|
|
84
|
+
* form.setValue('name', null); // Accepts null during editing
|
|
85
|
+
* form.reset({ name: null, age: null }); // Reset with null
|
|
86
|
+
*
|
|
87
|
+
* const onSubmit = (data: { name: string; age: number }) => {
|
|
88
|
+
* // ✅ data is exact type - no null | undefined
|
|
89
|
+
* console.log(data.name.toUpperCase()); // Safe to use string methods
|
|
90
|
+
* };
|
|
91
|
+
*
|
|
92
|
+
* return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* With default values
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const schema = z.object({
|
|
100
|
+
* username: z.string(),
|
|
101
|
+
* email: z.string().email(),
|
|
102
|
+
* notifications: z.boolean().default(true),
|
|
103
|
+
* }) satisfies z.ZodType<{
|
|
104
|
+
* username: string;
|
|
105
|
+
* email: string;
|
|
106
|
+
* notifications: boolean;
|
|
107
|
+
* }, any>;
|
|
108
|
+
*
|
|
109
|
+
* const form = useZodForm({
|
|
110
|
+
* schema,
|
|
111
|
+
* defaultValues: {
|
|
112
|
+
* username: '',
|
|
113
|
+
* email: '',
|
|
114
|
+
* // notifications gets default from schema
|
|
115
|
+
* },
|
|
17
116
|
* });
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* With optional and nullable fields
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const schema = z.object({
|
|
123
|
+
* title: z.string(),
|
|
124
|
+
* description: z.string().optional(), // Optional in output
|
|
125
|
+
* tags: z.array(z.string()).nullable(), // Nullable in output
|
|
126
|
+
* }) satisfies z.ZodType<{
|
|
127
|
+
* title: string;
|
|
128
|
+
* description?: string;
|
|
129
|
+
* tags: string[] | null;
|
|
130
|
+
* }, any>;
|
|
18
131
|
*
|
|
132
|
+
* const form = useZodForm({ schema });
|
|
133
|
+
*
|
|
134
|
+
* // All fields accept null/undefined during editing
|
|
135
|
+
* form.setValue('title', null);
|
|
136
|
+
* form.setValue('description', undefined);
|
|
137
|
+
* form.setValue('tags', null);
|
|
138
|
+
* ```
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* With zodResolver options
|
|
142
|
+
* ```typescript
|
|
19
143
|
* const form = useZodForm({
|
|
20
144
|
* schema,
|
|
21
|
-
*
|
|
145
|
+
* zodResolverOptions: {
|
|
146
|
+
* async: true, // Enable async validation
|
|
147
|
+
* errorMap: customErrorMap, // Custom error messages
|
|
148
|
+
* },
|
|
22
149
|
* });
|
|
23
150
|
* ```
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* Complete form example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const userSchema = z.object({
|
|
156
|
+
* name: z.string().min(1, 'Name is required'),
|
|
157
|
+
* email: z.string().email('Invalid email'),
|
|
158
|
+
* age: z.number().min(18, 'Must be 18+'),
|
|
159
|
+
* }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;
|
|
160
|
+
*
|
|
161
|
+
* function UserForm() {
|
|
162
|
+
* const form = useZodForm({
|
|
163
|
+
* schema: userSchema,
|
|
164
|
+
* defaultValues: { name: '', email: '', age: null },
|
|
165
|
+
* });
|
|
166
|
+
*
|
|
167
|
+
* const onSubmit = (data: { name: string; email: string; age: number }) => {
|
|
168
|
+
* // Type-safe: data has exact types, no null/undefined
|
|
169
|
+
* console.log(`${data.name} is ${data.age} years old`);
|
|
170
|
+
* };
|
|
171
|
+
*
|
|
172
|
+
* return (
|
|
173
|
+
* <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
174
|
+
* <input {...form.register('name')} />
|
|
175
|
+
* <input {...form.register('email')} type="email" />
|
|
176
|
+
* <input {...form.register('age', { valueAsNumber: true })} type="number" />
|
|
177
|
+
* <button type="submit">Submit</button>
|
|
178
|
+
* </form>
|
|
179
|
+
* );
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* @see {@link PartialWithNullableObjects} for the type transformation utility
|
|
184
|
+
* @see https://react-hook-form.com/docs/useform for React Hook Form documentation
|
|
185
|
+
* @see https://zod.dev for Zod schema documentation
|
|
186
|
+
* @since 0.1.0
|
|
24
187
|
*/
|
|
25
|
-
declare const useZodForm: <T extends FieldValues>({ schema, zodResolverOptions, ...formOptions }: {
|
|
26
|
-
schema:
|
|
27
|
-
defaultValues?: DefaultValues<
|
|
188
|
+
declare const useZodForm: <T extends FieldValues, I extends PartialWithAllNullables<T> = PartialWithNullableObjects<T>>({ schema, zodResolverOptions, ...formOptions }: {
|
|
189
|
+
schema: z.ZodType<T, I>;
|
|
190
|
+
defaultValues?: DefaultValues<I>;
|
|
28
191
|
zodResolverOptions?: Parameters<typeof zodResolver>[1];
|
|
29
|
-
} & Omit<UseFormProps<
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Japanese error map for Zod validation errors
|
|
33
|
-
* @param fieldName - Optional custom field name to use in error messages
|
|
34
|
-
* @returns Zod error map function
|
|
35
|
-
*/
|
|
36
|
-
declare function createJapaneseErrorMap(fieldName?: string): (issue: Parameters<ZodErrorMap>[0]) => string;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* English error map for Zod validation errors
|
|
40
|
-
* @param fieldName - Optional custom field name to use in error messages
|
|
41
|
-
* @returns Zod error map function
|
|
42
|
-
*/
|
|
43
|
-
declare function createEnglishErrorMap(fieldName?: string): (issue: Parameters<ZodErrorMap>[0]) => string;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Field namespace mapping for custom error messages
|
|
47
|
-
* You can extend this mapping to customize field names in error messages
|
|
48
|
-
*/
|
|
49
|
-
declare const FieldNamespaceMapping: {
|
|
50
|
-
department: {
|
|
51
|
-
groupName: string;
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
type FIELD_NAMESPACE = keyof typeof FieldNamespaceMapping;
|
|
55
|
-
/**
|
|
56
|
-
* Custom error resolver with field namespace support (Japanese locale)
|
|
57
|
-
* @deprecated Use createJapaneseErrorMap or createEnglishErrorMap instead
|
|
58
|
-
* @param options - Configuration options
|
|
59
|
-
* @param options.fieldNamespace - Namespace for field name mappings
|
|
60
|
-
* @returns Error resolver function
|
|
61
|
-
*/
|
|
62
|
-
declare const customErrorResolver: ({ fieldNamespace, }: {
|
|
63
|
-
fieldNamespace: FIELD_NAMESPACE;
|
|
64
|
-
}) => (issue: Parameters<ZodErrorMap>[0]) => string;
|
|
192
|
+
} & Omit<UseFormProps<I, unknown, T>, "resolver" | "defaultValues">) => react_hook_form.UseFormReturn<I, unknown, T>;
|
|
65
193
|
|
|
66
|
-
export { type
|
|
194
|
+
export { type PartialWithAllNullables, type PartialWithNullableObjects, useZodForm };
|