@zod-utils/react-hook-form 0.5.0 → 0.7.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 CHANGED
@@ -137,6 +137,34 @@ const form = useZodForm({
137
137
  - **Type inference**: No manual type annotations needed - everything is inferred from the schema
138
138
  - **Zod integration**: Automatically sets up `zodResolver` for validation
139
139
 
140
+ #### Using Without Default Values
141
+
142
+ The `defaultValues` parameter is **optional**. All form fields are automatically treated as optional during editing:
143
+
144
+ ```typescript
145
+ const schema = z.object({
146
+ name: z.string().min(1),
147
+ email: z.string().email(),
148
+ age: z.number(),
149
+ });
150
+
151
+ // ✅ No defaultValues needed!
152
+ const form = useZodForm({ schema });
153
+
154
+ // Fields can be set individually as the user types
155
+ form.setValue("name", "John");
156
+ form.setValue("email", "john@example.com");
157
+ form.setValue("age", 25);
158
+
159
+ // Validation still enforces the schema on submit
160
+ const onSubmit = form.handleSubmit((data) => {
161
+ // Type: { name: string; email: string; age: number }
162
+ console.log(data);
163
+ });
164
+ ```
165
+
166
+ This works because `useZodForm` uses the `Simplify` utility to ensure proper type inference, making all fields optional during editing while preserving exact types after validation.
167
+
140
168
  #### Custom Input Types
141
169
 
142
170
  You can override the default input type transformation if needed:
package/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Simplify } from '@zod-utils/core';
1
2
  export * from '@zod-utils/core';
2
3
  import * as react_hook_form from 'react-hook-form';
3
4
  import { FieldValues, DefaultValues, UseFormProps } from 'react-hook-form';
@@ -18,14 +19,24 @@ type AddNullToObjects<T> = {
18
19
  * - **Arrays**: optional → `type[] | undefined`
19
20
  * - **Objects**: optional and nullable → `type | null | undefined`
20
21
  *
22
+ * Uses {@link Simplify} to ensure TypeScript evaluates the type eagerly, which improves
23
+ * type inference and enables forms to work correctly without `defaultValues`.
24
+ *
21
25
  * @example
22
26
  * ```typescript
23
27
  * type User = { name: string; tags: string[]; profile: { bio: string } };
24
28
  * type FormInput = PartialWithNullableObjects<User>;
25
- * // { name?: string; tags?: string[]; profile?: { bio: string } | null; }
29
+ * // Evaluates to: { name?: string; tags?: string[]; profile?: { bio: string } | null; }
30
+ * ```
31
+ *
32
+ * @example
33
+ * Type inference with useZodForm
34
+ * ```typescript
35
+ * const schema = z.object({ name: z.string(), age: z.number() });
36
+ * const form = useZodForm({ schema }); // ✅ Works without defaultValues
26
37
  * ```
27
38
  */
28
- type PartialWithNullableObjects<T> = Partial<AddNullToObjects<T>>;
39
+ type PartialWithNullableObjects<T> = Simplify<Partial<AddNullToObjects<T>>>;
29
40
  /**
30
41
  * Makes all fields optional and nullable.
31
42
  *
@@ -47,8 +58,8 @@ type PartialWithAllNullables<T> = {
47
58
  *
48
59
  * This hook eliminates the TypeScript friction between React Hook Form's nullable field values
49
60
  * 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`)
61
+ * - **Input type** (`PartialWithNullableObjects<TOutput>`): Form fields accept `null | undefined` during editing
62
+ * - **Output type** (`TOutput`): Validated data matches exact schema type (no `null | undefined`)
52
63
  *
53
64
  * **Key Benefits:**
54
65
  * - ✅ No more "Type 'null' is not assignable to..." TypeScript errors
@@ -56,10 +67,11 @@ type PartialWithAllNullables<T> = {
56
67
  * - ✅ Validated output is still type-safe with exact Zod schema types
57
68
  * - ✅ Automatic zodResolver setup - no manual configuration needed
58
69
  *
59
- * @template T - The Zod schema output type (extends FieldValues)
70
+ * @template TOutput - The Zod schema output type (extends FieldValues)
71
+ * @template TInput - The Zod schema input type (accepts nullable/undefined values during form editing)
60
72
  *
61
73
  * @param options - Configuration object
62
- * @param options.schema - Zod schema with two-type signature `z.ZodType<T, PartialWithNullableObjects<T>>`
74
+ * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`
63
75
  * @param options.defaultValues - Default form values (accepts nullable/undefined values)
64
76
  * @param options.zodResolverOptions - Optional zodResolver configuration
65
77
  * @param options....formOptions - All other react-hook-form useForm options
@@ -117,6 +129,26 @@ type PartialWithAllNullables<T> = {
117
129
  * ```
118
130
  *
119
131
  * @example
132
+ * Without default values (all fields are optional during editing)
133
+ * ```typescript
134
+ * const schema = z.object({
135
+ * name: z.string().min(1),
136
+ * email: z.string().email(),
137
+ * age: z.number(),
138
+ * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;
139
+ *
140
+ * // ✅ No defaultValues needed - fields are optional during editing
141
+ * const form = useZodForm({ schema });
142
+ *
143
+ * // Form fields can be set individually as user types
144
+ * form.setValue('name', 'John');
145
+ * form.setValue('email', 'john@example.com');
146
+ * form.setValue('age', 25);
147
+ *
148
+ * // All fields must be valid on submit (per schema validation)
149
+ * ```
150
+ *
151
+ * @example
120
152
  * With optional and nullable fields
121
153
  * ```typescript
122
154
  * const schema = z.object({
@@ -185,10 +217,10 @@ type PartialWithAllNullables<T> = {
185
217
  * @see https://zod.dev for Zod schema documentation
186
218
  * @since 0.1.0
187
219
  */
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>;
220
+ declare const useZodForm: <TOutput extends FieldValues, TFormInput extends PartialWithAllNullables<TOutput> = PartialWithNullableObjects<TOutput>, TInput extends TFormInput = TFormInput>({ schema, zodResolverOptions, ...formOptions }: {
221
+ schema: z.ZodType<TOutput, TInput>;
222
+ defaultValues?: DefaultValues<TFormInput>;
191
223
  zodResolverOptions?: Parameters<typeof zodResolver>[1];
192
- } & Omit<UseFormProps<I, unknown, T>, "resolver" | "defaultValues">) => react_hook_form.UseFormReturn<I, unknown, T>;
224
+ } & Omit<UseFormProps<TFormInput, unknown, TOutput>, "resolver" | "defaultValues">) => react_hook_form.UseFormReturn<TFormInput, unknown, TOutput>;
193
225
 
194
226
  export { type PartialWithAllNullables, type PartialWithNullableObjects, useZodForm };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Simplify } from '@zod-utils/core';
1
2
  export * from '@zod-utils/core';
2
3
  import * as react_hook_form from 'react-hook-form';
3
4
  import { FieldValues, DefaultValues, UseFormProps } from 'react-hook-form';
@@ -18,14 +19,24 @@ type AddNullToObjects<T> = {
18
19
  * - **Arrays**: optional → `type[] | undefined`
19
20
  * - **Objects**: optional and nullable → `type | null | undefined`
20
21
  *
22
+ * Uses {@link Simplify} to ensure TypeScript evaluates the type eagerly, which improves
23
+ * type inference and enables forms to work correctly without `defaultValues`.
24
+ *
21
25
  * @example
22
26
  * ```typescript
23
27
  * type User = { name: string; tags: string[]; profile: { bio: string } };
24
28
  * type FormInput = PartialWithNullableObjects<User>;
25
- * // { name?: string; tags?: string[]; profile?: { bio: string } | null; }
29
+ * // Evaluates to: { name?: string; tags?: string[]; profile?: { bio: string } | null; }
30
+ * ```
31
+ *
32
+ * @example
33
+ * Type inference with useZodForm
34
+ * ```typescript
35
+ * const schema = z.object({ name: z.string(), age: z.number() });
36
+ * const form = useZodForm({ schema }); // ✅ Works without defaultValues
26
37
  * ```
27
38
  */
28
- type PartialWithNullableObjects<T> = Partial<AddNullToObjects<T>>;
39
+ type PartialWithNullableObjects<T> = Simplify<Partial<AddNullToObjects<T>>>;
29
40
  /**
30
41
  * Makes all fields optional and nullable.
31
42
  *
@@ -47,8 +58,8 @@ type PartialWithAllNullables<T> = {
47
58
  *
48
59
  * This hook eliminates the TypeScript friction between React Hook Form's nullable field values
49
60
  * 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`)
61
+ * - **Input type** (`PartialWithNullableObjects<TOutput>`): Form fields accept `null | undefined` during editing
62
+ * - **Output type** (`TOutput`): Validated data matches exact schema type (no `null | undefined`)
52
63
  *
53
64
  * **Key Benefits:**
54
65
  * - ✅ No more "Type 'null' is not assignable to..." TypeScript errors
@@ -56,10 +67,11 @@ type PartialWithAllNullables<T> = {
56
67
  * - ✅ Validated output is still type-safe with exact Zod schema types
57
68
  * - ✅ Automatic zodResolver setup - no manual configuration needed
58
69
  *
59
- * @template T - The Zod schema output type (extends FieldValues)
70
+ * @template TOutput - The Zod schema output type (extends FieldValues)
71
+ * @template TInput - The Zod schema input type (accepts nullable/undefined values during form editing)
60
72
  *
61
73
  * @param options - Configuration object
62
- * @param options.schema - Zod schema with two-type signature `z.ZodType<T, PartialWithNullableObjects<T>>`
74
+ * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`
63
75
  * @param options.defaultValues - Default form values (accepts nullable/undefined values)
64
76
  * @param options.zodResolverOptions - Optional zodResolver configuration
65
77
  * @param options....formOptions - All other react-hook-form useForm options
@@ -117,6 +129,26 @@ type PartialWithAllNullables<T> = {
117
129
  * ```
118
130
  *
119
131
  * @example
132
+ * Without default values (all fields are optional during editing)
133
+ * ```typescript
134
+ * const schema = z.object({
135
+ * name: z.string().min(1),
136
+ * email: z.string().email(),
137
+ * age: z.number(),
138
+ * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;
139
+ *
140
+ * // ✅ No defaultValues needed - fields are optional during editing
141
+ * const form = useZodForm({ schema });
142
+ *
143
+ * // Form fields can be set individually as user types
144
+ * form.setValue('name', 'John');
145
+ * form.setValue('email', 'john@example.com');
146
+ * form.setValue('age', 25);
147
+ *
148
+ * // All fields must be valid on submit (per schema validation)
149
+ * ```
150
+ *
151
+ * @example
120
152
  * With optional and nullable fields
121
153
  * ```typescript
122
154
  * const schema = z.object({
@@ -185,10 +217,10 @@ type PartialWithAllNullables<T> = {
185
217
  * @see https://zod.dev for Zod schema documentation
186
218
  * @since 0.1.0
187
219
  */
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>;
220
+ declare const useZodForm: <TOutput extends FieldValues, TFormInput extends PartialWithAllNullables<TOutput> = PartialWithNullableObjects<TOutput>, TInput extends TFormInput = TFormInput>({ schema, zodResolverOptions, ...formOptions }: {
221
+ schema: z.ZodType<TOutput, TInput>;
222
+ defaultValues?: DefaultValues<TFormInput>;
191
223
  zodResolverOptions?: Parameters<typeof zodResolver>[1];
192
- } & Omit<UseFormProps<I, unknown, T>, "resolver" | "defaultValues">) => react_hook_form.UseFormReturn<I, unknown, T>;
224
+ } & Omit<UseFormProps<TFormInput, unknown, TOutput>, "resolver" | "defaultValues">) => react_hook_form.UseFormReturn<TFormInput, unknown, TOutput>;
193
225
 
194
226
  export { type PartialWithAllNullables, type PartialWithNullableObjects, useZodForm };
package/dist/index.js CHANGED
@@ -40,7 +40,10 @@ var useZodForm = (_a) => {
40
40
  "schema",
41
41
  "zodResolverOptions"
42
42
  ]);
43
- const resolver = zod.zodResolver(schema, zodResolverOptions);
43
+ const resolver = zod.zodResolver(
44
+ schema,
45
+ zodResolverOptions
46
+ );
44
47
  return reactHookForm.useForm(__spreadValues({
45
48
  resolver
46
49
  }, formOptions));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/use-zod-form.ts"],"names":["zodResolver","useForm"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4JO,IAAM,UAAA,GAAa,CAGxB,EAAA,KAQsE;AARtE,EAAA,IAAA,EAAA,GAAA,EAAA,EACA;AAAA,IAAA,MAAA;AAAA,IACA;AAAA,GAjKF,GA+JE,EAAA,EAGG,WAAA,GAAA,SAAA,CAHH,EAAA,EAGG;AAAA,IAFH,QAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAOA,EAAA,MAAM,QAAA,GAAWA,eAAA,CAAY,MAAA,EAAQ,kBAAkB,CAAA;AAEvD,EAAA,OAAOC,qBAAA,CAAQ,cAAA,CAAA;AAAA,IACb;AAAA,GAAA,EACG,WAAA,CACJ,CAAA;AACH","file":"index.js","sourcesContent":["import { zodResolver } from '@hookform/resolvers/zod';\nimport {\n type DefaultValues,\n type FieldValues,\n type UseFormProps,\n useForm,\n} from 'react-hook-form';\nimport type { z } from 'zod';\nimport type {\n PartialWithAllNullables,\n PartialWithNullableObjects,\n} from './types';\n\n/**\n * Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.\n *\n * This hook eliminates the TypeScript friction between React Hook Form's nullable field values\n * and Zod's strict output types. It uses a two-type schema pattern where:\n * - **Input type** (`PartialWithNullableObjects<T>`): Form fields accept `null | undefined` during editing\n * - **Output type** (`T`): Validated data matches exact schema type (no `null | undefined`)\n *\n * **Key Benefits:**\n * - ✅ No more \"Type 'null' is not assignable to...\" TypeScript errors\n * - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely\n * - ✅ Validated output is still type-safe with exact Zod schema types\n * - ✅ Automatic zodResolver setup - no manual configuration needed\n *\n * @template T - The Zod schema output type (extends FieldValues)\n *\n * @param options - Configuration object\n * @param options.schema - Zod schema with two-type signature `z.ZodType<T, PartialWithNullableObjects<T>>`\n * @param options.defaultValues - Default form values (accepts nullable/undefined values)\n * @param options.zodResolverOptions - Optional zodResolver configuration\n * @param options....formOptions - All other react-hook-form useForm options\n *\n * @returns React Hook Form instance with type-safe methods\n *\n * @example\n * Basic usage with required fields\n * ```typescript\n * import { useZodForm } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * name: z.string().min(1), // Required field\n * age: z.number().min(0),\n * }) satisfies z.ZodType<{ name: string; age: number }, any>;\n *\n * function MyForm() {\n * const form = useZodForm({ schema });\n *\n * // ✅ These work without type errors:\n * form.setValue('name', null); // Accepts null during editing\n * form.reset({ name: null, age: null }); // Reset with null\n *\n * const onSubmit = (data: { name: string; age: number }) => {\n * // ✅ data is exact type - no null | undefined\n * console.log(data.name.toUpperCase()); // Safe to use string methods\n * };\n *\n * return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;\n * }\n * ```\n *\n * @example\n * With default values\n * ```typescript\n * const schema = z.object({\n * username: z.string(),\n * email: z.string().email(),\n * notifications: z.boolean().default(true),\n * }) satisfies z.ZodType<{\n * username: string;\n * email: string;\n * notifications: boolean;\n * }, any>;\n *\n * const form = useZodForm({\n * schema,\n * defaultValues: {\n * username: '',\n * email: '',\n * // notifications gets default from schema\n * },\n * });\n * ```\n *\n * @example\n * With optional and nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string(),\n * description: z.string().optional(), // Optional in output\n * tags: z.array(z.string()).nullable(), // Nullable in output\n * }) satisfies z.ZodType<{\n * title: string;\n * description?: string;\n * tags: string[] | null;\n * }, any>;\n *\n * const form = useZodForm({ schema });\n *\n * // All fields accept null/undefined during editing\n * form.setValue('title', null);\n * form.setValue('description', undefined);\n * form.setValue('tags', null);\n * ```\n *\n * @example\n * With zodResolver options\n * ```typescript\n * const form = useZodForm({\n * schema,\n * zodResolverOptions: {\n * async: true, // Enable async validation\n * errorMap: customErrorMap, // Custom error messages\n * },\n * });\n * ```\n *\n * @example\n * Complete form example\n * ```typescript\n * const userSchema = z.object({\n * name: z.string().min(1, 'Name is required'),\n * email: z.string().email('Invalid email'),\n * age: z.number().min(18, 'Must be 18+'),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * function UserForm() {\n * const form = useZodForm({\n * schema: userSchema,\n * defaultValues: { name: '', email: '', age: null },\n * });\n *\n * const onSubmit = (data: { name: string; email: string; age: number }) => {\n * // Type-safe: data has exact types, no null/undefined\n * console.log(`${data.name} is ${data.age} years old`);\n * };\n *\n * return (\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <input {...form.register('name')} />\n * <input {...form.register('email')} type=\"email\" />\n * <input {...form.register('age', { valueAsNumber: true })} type=\"number\" />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n *\n * @see {@link PartialWithNullableObjects} for the type transformation utility\n * @see https://react-hook-form.com/docs/useform for React Hook Form documentation\n * @see https://zod.dev for Zod schema documentation\n * @since 0.1.0\n */\nexport const useZodForm = <\n T extends FieldValues,\n I extends PartialWithAllNullables<T> = PartialWithNullableObjects<T>,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<T, I>;\n defaultValues?: DefaultValues<I>;\n zodResolverOptions?: Parameters<typeof zodResolver>[1];\n} & Omit<UseFormProps<I, unknown, T>, 'resolver' | 'defaultValues'>) => {\n const resolver = zodResolver(schema, zodResolverOptions);\n\n return useForm({\n resolver,\n ...formOptions,\n });\n};\n"]}
1
+ {"version":3,"sources":["../src/use-zod-form.ts"],"names":["zodResolver","useForm"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiLO,IAAM,UAAA,GAAa,CAKxB,EAAA,KAWI;AAXJ,EAAA,IAAA,EAAA,GAAA,EAAA,EACA;AAAA,IAAA,MAAA;AAAA,IACA;AAAA,GAxLF,GAsLE,EAAA,EAGG,WAAA,GAAA,SAAA,CAHH,EAAA,EAGG;AAAA,IAFH,QAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAUA,EAAA,MAAM,QAAA,GAAWA,eAAA;AAAA,IACf,MAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAOC,qBAAA,CAAQ,cAAA,CAAA;AAAA,IACb;AAAA,GAAA,EACG,WAAA,CACJ,CAAA;AACH","file":"index.js","sourcesContent":["import { zodResolver } from '@hookform/resolvers/zod';\nimport {\n type DefaultValues,\n type FieldValues,\n type UseFormProps,\n useForm,\n} from 'react-hook-form';\nimport type { z } from 'zod';\nimport type {\n PartialWithAllNullables,\n PartialWithNullableObjects,\n} from './types';\n\n/**\n * Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.\n *\n * This hook eliminates the TypeScript friction between React Hook Form's nullable field values\n * and Zod's strict output types. It uses a two-type schema pattern where:\n * - **Input type** (`PartialWithNullableObjects<TOutput>`): Form fields accept `null | undefined` during editing\n * - **Output type** (`TOutput`): Validated data matches exact schema type (no `null | undefined`)\n *\n * **Key Benefits:**\n * - ✅ No more \"Type 'null' is not assignable to...\" TypeScript errors\n * - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely\n * - ✅ Validated output is still type-safe with exact Zod schema types\n * - ✅ Automatic zodResolver setup - no manual configuration needed\n *\n * @template TOutput - The Zod schema output type (extends FieldValues)\n * @template TInput - The Zod schema input type (accepts nullable/undefined values during form editing)\n *\n * @param options - Configuration object\n * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`\n * @param options.defaultValues - Default form values (accepts nullable/undefined values)\n * @param options.zodResolverOptions - Optional zodResolver configuration\n * @param options....formOptions - All other react-hook-form useForm options\n *\n * @returns React Hook Form instance with type-safe methods\n *\n * @example\n * Basic usage with required fields\n * ```typescript\n * import { useZodForm } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * name: z.string().min(1), // Required field\n * age: z.number().min(0),\n * }) satisfies z.ZodType<{ name: string; age: number }, any>;\n *\n * function MyForm() {\n * const form = useZodForm({ schema });\n *\n * // ✅ These work without type errors:\n * form.setValue('name', null); // Accepts null during editing\n * form.reset({ name: null, age: null }); // Reset with null\n *\n * const onSubmit = (data: { name: string; age: number }) => {\n * // ✅ data is exact type - no null | undefined\n * console.log(data.name.toUpperCase()); // Safe to use string methods\n * };\n *\n * return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;\n * }\n * ```\n *\n * @example\n * With default values\n * ```typescript\n * const schema = z.object({\n * username: z.string(),\n * email: z.string().email(),\n * notifications: z.boolean().default(true),\n * }) satisfies z.ZodType<{\n * username: string;\n * email: string;\n * notifications: boolean;\n * }, any>;\n *\n * const form = useZodForm({\n * schema,\n * defaultValues: {\n * username: '',\n * email: '',\n * // notifications gets default from schema\n * },\n * });\n * ```\n *\n * @example\n * Without default values (all fields are optional during editing)\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * age: z.number(),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * // ✅ No defaultValues needed - fields are optional during editing\n * const form = useZodForm({ schema });\n *\n * // Form fields can be set individually as user types\n * form.setValue('name', 'John');\n * form.setValue('email', 'john@example.com');\n * form.setValue('age', 25);\n *\n * // All fields must be valid on submit (per schema validation)\n * ```\n *\n * @example\n * With optional and nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string(),\n * description: z.string().optional(), // Optional in output\n * tags: z.array(z.string()).nullable(), // Nullable in output\n * }) satisfies z.ZodType<{\n * title: string;\n * description?: string;\n * tags: string[] | null;\n * }, any>;\n *\n * const form = useZodForm({ schema });\n *\n * // All fields accept null/undefined during editing\n * form.setValue('title', null);\n * form.setValue('description', undefined);\n * form.setValue('tags', null);\n * ```\n *\n * @example\n * With zodResolver options\n * ```typescript\n * const form = useZodForm({\n * schema,\n * zodResolverOptions: {\n * async: true, // Enable async validation\n * errorMap: customErrorMap, // Custom error messages\n * },\n * });\n * ```\n *\n * @example\n * Complete form example\n * ```typescript\n * const userSchema = z.object({\n * name: z.string().min(1, 'Name is required'),\n * email: z.string().email('Invalid email'),\n * age: z.number().min(18, 'Must be 18+'),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * function UserForm() {\n * const form = useZodForm({\n * schema: userSchema,\n * defaultValues: { name: '', email: '', age: null },\n * });\n *\n * const onSubmit = (data: { name: string; email: string; age: number }) => {\n * // Type-safe: data has exact types, no null/undefined\n * console.log(`${data.name} is ${data.age} years old`);\n * };\n *\n * return (\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <input {...form.register('name')} />\n * <input {...form.register('email')} type=\"email\" />\n * <input {...form.register('age', { valueAsNumber: true })} type=\"number\" />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n *\n * @see {@link PartialWithNullableObjects} for the type transformation utility\n * @see https://react-hook-form.com/docs/useform for React Hook Form documentation\n * @see https://zod.dev for Zod schema documentation\n * @since 0.1.0\n */\nexport const useZodForm = <\n TOutput extends FieldValues,\n TFormInput extends\n PartialWithAllNullables<TOutput> = PartialWithNullableObjects<TOutput>,\n TInput extends TFormInput = TFormInput,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<TOutput, TInput>;\n defaultValues?: DefaultValues<TFormInput>;\n zodResolverOptions?: Parameters<typeof zodResolver>[1];\n} & Omit<\n UseFormProps<TFormInput, unknown, TOutput>,\n 'resolver' | 'defaultValues'\n>) => {\n const resolver = zodResolver<TFormInput, unknown, TOutput>(\n schema,\n zodResolverOptions,\n );\n\n return useForm({\n resolver,\n ...formOptions,\n });\n};\n"]}
package/dist/index.mjs CHANGED
@@ -38,7 +38,10 @@ var useZodForm = (_a) => {
38
38
  "schema",
39
39
  "zodResolverOptions"
40
40
  ]);
41
- const resolver = zodResolver(schema, zodResolverOptions);
41
+ const resolver = zodResolver(
42
+ schema,
43
+ zodResolverOptions
44
+ );
42
45
  return useForm(__spreadValues({
43
46
  resolver
44
47
  }, formOptions));
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/use-zod-form.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4JO,IAAM,UAAA,GAAa,CAGxB,EAAA,KAQsE;AARtE,EAAA,IAAA,EAAA,GAAA,EAAA,EACA;AAAA,IAAA,MAAA;AAAA,IACA;AAAA,GAjKF,GA+JE,EAAA,EAGG,WAAA,GAAA,SAAA,CAHH,EAAA,EAGG;AAAA,IAFH,QAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAOA,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,MAAA,EAAQ,kBAAkB,CAAA;AAEvD,EAAA,OAAO,OAAA,CAAQ,cAAA,CAAA;AAAA,IACb;AAAA,GAAA,EACG,WAAA,CACJ,CAAA;AACH","file":"index.mjs","sourcesContent":["import { zodResolver } from '@hookform/resolvers/zod';\nimport {\n type DefaultValues,\n type FieldValues,\n type UseFormProps,\n useForm,\n} from 'react-hook-form';\nimport type { z } from 'zod';\nimport type {\n PartialWithAllNullables,\n PartialWithNullableObjects,\n} from './types';\n\n/**\n * Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.\n *\n * This hook eliminates the TypeScript friction between React Hook Form's nullable field values\n * and Zod's strict output types. It uses a two-type schema pattern where:\n * - **Input type** (`PartialWithNullableObjects<T>`): Form fields accept `null | undefined` during editing\n * - **Output type** (`T`): Validated data matches exact schema type (no `null | undefined`)\n *\n * **Key Benefits:**\n * - ✅ No more \"Type 'null' is not assignable to...\" TypeScript errors\n * - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely\n * - ✅ Validated output is still type-safe with exact Zod schema types\n * - ✅ Automatic zodResolver setup - no manual configuration needed\n *\n * @template T - The Zod schema output type (extends FieldValues)\n *\n * @param options - Configuration object\n * @param options.schema - Zod schema with two-type signature `z.ZodType<T, PartialWithNullableObjects<T>>`\n * @param options.defaultValues - Default form values (accepts nullable/undefined values)\n * @param options.zodResolverOptions - Optional zodResolver configuration\n * @param options....formOptions - All other react-hook-form useForm options\n *\n * @returns React Hook Form instance with type-safe methods\n *\n * @example\n * Basic usage with required fields\n * ```typescript\n * import { useZodForm } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * name: z.string().min(1), // Required field\n * age: z.number().min(0),\n * }) satisfies z.ZodType<{ name: string; age: number }, any>;\n *\n * function MyForm() {\n * const form = useZodForm({ schema });\n *\n * // ✅ These work without type errors:\n * form.setValue('name', null); // Accepts null during editing\n * form.reset({ name: null, age: null }); // Reset with null\n *\n * const onSubmit = (data: { name: string; age: number }) => {\n * // ✅ data is exact type - no null | undefined\n * console.log(data.name.toUpperCase()); // Safe to use string methods\n * };\n *\n * return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;\n * }\n * ```\n *\n * @example\n * With default values\n * ```typescript\n * const schema = z.object({\n * username: z.string(),\n * email: z.string().email(),\n * notifications: z.boolean().default(true),\n * }) satisfies z.ZodType<{\n * username: string;\n * email: string;\n * notifications: boolean;\n * }, any>;\n *\n * const form = useZodForm({\n * schema,\n * defaultValues: {\n * username: '',\n * email: '',\n * // notifications gets default from schema\n * },\n * });\n * ```\n *\n * @example\n * With optional and nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string(),\n * description: z.string().optional(), // Optional in output\n * tags: z.array(z.string()).nullable(), // Nullable in output\n * }) satisfies z.ZodType<{\n * title: string;\n * description?: string;\n * tags: string[] | null;\n * }, any>;\n *\n * const form = useZodForm({ schema });\n *\n * // All fields accept null/undefined during editing\n * form.setValue('title', null);\n * form.setValue('description', undefined);\n * form.setValue('tags', null);\n * ```\n *\n * @example\n * With zodResolver options\n * ```typescript\n * const form = useZodForm({\n * schema,\n * zodResolverOptions: {\n * async: true, // Enable async validation\n * errorMap: customErrorMap, // Custom error messages\n * },\n * });\n * ```\n *\n * @example\n * Complete form example\n * ```typescript\n * const userSchema = z.object({\n * name: z.string().min(1, 'Name is required'),\n * email: z.string().email('Invalid email'),\n * age: z.number().min(18, 'Must be 18+'),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * function UserForm() {\n * const form = useZodForm({\n * schema: userSchema,\n * defaultValues: { name: '', email: '', age: null },\n * });\n *\n * const onSubmit = (data: { name: string; email: string; age: number }) => {\n * // Type-safe: data has exact types, no null/undefined\n * console.log(`${data.name} is ${data.age} years old`);\n * };\n *\n * return (\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <input {...form.register('name')} />\n * <input {...form.register('email')} type=\"email\" />\n * <input {...form.register('age', { valueAsNumber: true })} type=\"number\" />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n *\n * @see {@link PartialWithNullableObjects} for the type transformation utility\n * @see https://react-hook-form.com/docs/useform for React Hook Form documentation\n * @see https://zod.dev for Zod schema documentation\n * @since 0.1.0\n */\nexport const useZodForm = <\n T extends FieldValues,\n I extends PartialWithAllNullables<T> = PartialWithNullableObjects<T>,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<T, I>;\n defaultValues?: DefaultValues<I>;\n zodResolverOptions?: Parameters<typeof zodResolver>[1];\n} & Omit<UseFormProps<I, unknown, T>, 'resolver' | 'defaultValues'>) => {\n const resolver = zodResolver(schema, zodResolverOptions);\n\n return useForm({\n resolver,\n ...formOptions,\n });\n};\n"]}
1
+ {"version":3,"sources":["../src/use-zod-form.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiLO,IAAM,UAAA,GAAa,CAKxB,EAAA,KAWI;AAXJ,EAAA,IAAA,EAAA,GAAA,EAAA,EACA;AAAA,IAAA,MAAA;AAAA,IACA;AAAA,GAxLF,GAsLE,EAAA,EAGG,WAAA,GAAA,SAAA,CAHH,EAAA,EAGG;AAAA,IAFH,QAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAUA,EAAA,MAAM,QAAA,GAAW,WAAA;AAAA,IACf,MAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,OAAA,CAAQ,cAAA,CAAA;AAAA,IACb;AAAA,GAAA,EACG,WAAA,CACJ,CAAA;AACH","file":"index.mjs","sourcesContent":["import { zodResolver } from '@hookform/resolvers/zod';\nimport {\n type DefaultValues,\n type FieldValues,\n type UseFormProps,\n useForm,\n} from 'react-hook-form';\nimport type { z } from 'zod';\nimport type {\n PartialWithAllNullables,\n PartialWithNullableObjects,\n} from './types';\n\n/**\n * Type-safe React Hook Form wrapper with automatic Zod v4 schema validation and type transformation.\n *\n * This hook eliminates the TypeScript friction between React Hook Form's nullable field values\n * and Zod's strict output types. It uses a two-type schema pattern where:\n * - **Input type** (`PartialWithNullableObjects<TOutput>`): Form fields accept `null | undefined` during editing\n * - **Output type** (`TOutput`): Validated data matches exact schema type (no `null | undefined`)\n *\n * **Key Benefits:**\n * - ✅ No more \"Type 'null' is not assignable to...\" TypeScript errors\n * - ✅ Use `form.setValue()` and `form.reset()` with `null` values freely\n * - ✅ Validated output is still type-safe with exact Zod schema types\n * - ✅ Automatic zodResolver setup - no manual configuration needed\n *\n * @template TOutput - The Zod schema output type (extends FieldValues)\n * @template TInput - The Zod schema input type (accepts nullable/undefined values during form editing)\n *\n * @param options - Configuration object\n * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`\n * @param options.defaultValues - Default form values (accepts nullable/undefined values)\n * @param options.zodResolverOptions - Optional zodResolver configuration\n * @param options....formOptions - All other react-hook-form useForm options\n *\n * @returns React Hook Form instance with type-safe methods\n *\n * @example\n * Basic usage with required fields\n * ```typescript\n * import { useZodForm } from '@zod-utils/react-hook-form';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * name: z.string().min(1), // Required field\n * age: z.number().min(0),\n * }) satisfies z.ZodType<{ name: string; age: number }, any>;\n *\n * function MyForm() {\n * const form = useZodForm({ schema });\n *\n * // ✅ These work without type errors:\n * form.setValue('name', null); // Accepts null during editing\n * form.reset({ name: null, age: null }); // Reset with null\n *\n * const onSubmit = (data: { name: string; age: number }) => {\n * // ✅ data is exact type - no null | undefined\n * console.log(data.name.toUpperCase()); // Safe to use string methods\n * };\n *\n * return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;\n * }\n * ```\n *\n * @example\n * With default values\n * ```typescript\n * const schema = z.object({\n * username: z.string(),\n * email: z.string().email(),\n * notifications: z.boolean().default(true),\n * }) satisfies z.ZodType<{\n * username: string;\n * email: string;\n * notifications: boolean;\n * }, any>;\n *\n * const form = useZodForm({\n * schema,\n * defaultValues: {\n * username: '',\n * email: '',\n * // notifications gets default from schema\n * },\n * });\n * ```\n *\n * @example\n * Without default values (all fields are optional during editing)\n * ```typescript\n * const schema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * age: z.number(),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * // ✅ No defaultValues needed - fields are optional during editing\n * const form = useZodForm({ schema });\n *\n * // Form fields can be set individually as user types\n * form.setValue('name', 'John');\n * form.setValue('email', 'john@example.com');\n * form.setValue('age', 25);\n *\n * // All fields must be valid on submit (per schema validation)\n * ```\n *\n * @example\n * With optional and nullable fields\n * ```typescript\n * const schema = z.object({\n * title: z.string(),\n * description: z.string().optional(), // Optional in output\n * tags: z.array(z.string()).nullable(), // Nullable in output\n * }) satisfies z.ZodType<{\n * title: string;\n * description?: string;\n * tags: string[] | null;\n * }, any>;\n *\n * const form = useZodForm({ schema });\n *\n * // All fields accept null/undefined during editing\n * form.setValue('title', null);\n * form.setValue('description', undefined);\n * form.setValue('tags', null);\n * ```\n *\n * @example\n * With zodResolver options\n * ```typescript\n * const form = useZodForm({\n * schema,\n * zodResolverOptions: {\n * async: true, // Enable async validation\n * errorMap: customErrorMap, // Custom error messages\n * },\n * });\n * ```\n *\n * @example\n * Complete form example\n * ```typescript\n * const userSchema = z.object({\n * name: z.string().min(1, 'Name is required'),\n * email: z.string().email('Invalid email'),\n * age: z.number().min(18, 'Must be 18+'),\n * }) satisfies z.ZodType<{ name: string; email: string; age: number }, any>;\n *\n * function UserForm() {\n * const form = useZodForm({\n * schema: userSchema,\n * defaultValues: { name: '', email: '', age: null },\n * });\n *\n * const onSubmit = (data: { name: string; email: string; age: number }) => {\n * // Type-safe: data has exact types, no null/undefined\n * console.log(`${data.name} is ${data.age} years old`);\n * };\n *\n * return (\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <input {...form.register('name')} />\n * <input {...form.register('email')} type=\"email\" />\n * <input {...form.register('age', { valueAsNumber: true })} type=\"number\" />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n *\n * @see {@link PartialWithNullableObjects} for the type transformation utility\n * @see https://react-hook-form.com/docs/useform for React Hook Form documentation\n * @see https://zod.dev for Zod schema documentation\n * @since 0.1.0\n */\nexport const useZodForm = <\n TOutput extends FieldValues,\n TFormInput extends\n PartialWithAllNullables<TOutput> = PartialWithNullableObjects<TOutput>,\n TInput extends TFormInput = TFormInput,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<TOutput, TInput>;\n defaultValues?: DefaultValues<TFormInput>;\n zodResolverOptions?: Parameters<typeof zodResolver>[1];\n} & Omit<\n UseFormProps<TFormInput, unknown, TOutput>,\n 'resolver' | 'defaultValues'\n>) => {\n const resolver = zodResolver<TFormInput, unknown, TOutput>(\n schema,\n zodResolverOptions,\n );\n\n return useForm({\n resolver,\n ...formOptions,\n });\n};\n"]}
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@zod-utils/react-hook-form",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "React Hook Form integration and utilities for Zod schemas",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "development": "./src/index.ts",
11
10
  "types": "./dist/index.d.ts",
12
11
  "import": "./dist/index.mjs",
13
12
  "require": "./dist/index.js"