@zod-utils/react-hook-form 0.7.0 → 0.8.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
@@ -165,6 +165,71 @@ const onSubmit = form.handleSubmit((data) => {
165
165
 
166
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
167
 
168
+ #### Default Values Type Safety
169
+
170
+ The `defaultValues` parameter enforces **shallow partial** typing for type safety. This means:
171
+
172
+ - ✅ **Top-level fields** can be omitted or set to `null`/`undefined`
173
+ - ✅ **Nested objects** can be omitted entirely
174
+ - ❌ **Nested objects** cannot be partially filled - they must be complete if provided
175
+
176
+ ```typescript
177
+ const schema = z.object({
178
+ user: z.object({
179
+ name: z.string(),
180
+ email: z.string().email(),
181
+ age: z.number(),
182
+ }),
183
+ settings: z.object({
184
+ theme: z.enum(["light", "dark"]),
185
+ notifications: z.boolean(),
186
+ }),
187
+ });
188
+
189
+ // ✅ Correct: Omit nested objects
190
+ const form = useZodForm({
191
+ schema,
192
+ defaultValues: {
193
+ // settings omitted - OK
194
+ },
195
+ });
196
+
197
+ // ✅ Correct: Provide complete nested objects
198
+ const form = useZodForm({
199
+ schema,
200
+ defaultValues: {
201
+ user: { name: "John", email: "john@example.com", age: 30 }, // Complete
202
+ settings: { theme: "dark", notifications: true }, // Complete
203
+ },
204
+ });
205
+
206
+ // ❌ TypeScript Error: Partial nested objects not allowed
207
+ const form = useZodForm({
208
+ schema,
209
+ defaultValues: {
210
+ user: { name: "John" }, // ❌ Missing email and age
211
+ },
212
+ });
213
+ ```
214
+
215
+ **Why this restriction?** This prevents type errors where partial nested objects might be missing required properties. If you need to provide partial nested defaults, use `getSchemaDefaults()` which handles this correctly:
216
+
217
+ ```typescript
218
+ const form = useZodForm({
219
+ schema,
220
+ defaultValues: getSchemaDefaults(schema), // ✅ Type-safe partial defaults
221
+ });
222
+ ```
223
+
224
+ **Note:** This restriction only applies to the `defaultValues` parameter. During form editing, all fields still accept `null`/`undefined` as expected:
225
+
226
+ ```typescript
227
+ // ✅ Works! Form inputs still accept null/undefined
228
+ form.setValue("user", null);
229
+ form.setValue("settings", null);
230
+ form.reset({ user: null, settings: undefined });
231
+ ```
232
+
168
233
  #### Custom Input Types
169
234
 
170
235
  You can override the default input type transformation if needed:
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Simplify } from '@zod-utils/core';
2
2
  export * from '@zod-utils/core';
3
3
  import * as react_hook_form from 'react-hook-form';
4
- import { FieldValues, DefaultValues, UseFormProps } from 'react-hook-form';
4
+ import { FieldValues, UseFormProps } from 'react-hook-form';
5
5
  import { zodResolver } from '@hookform/resolvers/zod';
6
6
  import { z } from 'zod';
7
7
 
@@ -72,7 +72,7 @@ type PartialWithAllNullables<T> = {
72
72
  *
73
73
  * @param options - Configuration object
74
74
  * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`
75
- * @param options.defaultValues - Default form values (accepts nullable/undefined values)
75
+ * @param options.defaultValues - Default form values (shallow partial - nested objects must be complete if provided)
76
76
  * @param options.zodResolverOptions - Optional zodResolver configuration
77
77
  * @param options....formOptions - All other react-hook-form useForm options
78
78
  *
@@ -217,9 +217,9 @@ type PartialWithAllNullables<T> = {
217
217
  * @see https://zod.dev for Zod schema documentation
218
218
  * @since 0.1.0
219
219
  */
220
- declare const useZodForm: <TOutput extends FieldValues, TFormInput extends PartialWithAllNullables<TOutput> = PartialWithNullableObjects<TOutput>, TInput extends TFormInput = TFormInput>({ schema, zodResolverOptions, ...formOptions }: {
220
+ declare const useZodForm: <TOutput extends FieldValues, TFormInput extends PartialWithAllNullables<TOutput> = PartialWithNullableObjects<TOutput>, TInput extends TFormInput = TFormInput, TDefault extends TFormInput = TFormInput>({ schema, zodResolverOptions, ...formOptions }: {
221
221
  schema: z.ZodType<TOutput, TInput>;
222
- defaultValues?: DefaultValues<TFormInput>;
222
+ defaultValues?: TDefault;
223
223
  zodResolverOptions?: Parameters<typeof zodResolver>[1];
224
224
  } & Omit<UseFormProps<TFormInput, unknown, TOutput>, "resolver" | "defaultValues">) => react_hook_form.UseFormReturn<TFormInput, unknown, TOutput>;
225
225
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Simplify } from '@zod-utils/core';
2
2
  export * from '@zod-utils/core';
3
3
  import * as react_hook_form from 'react-hook-form';
4
- import { FieldValues, DefaultValues, UseFormProps } from 'react-hook-form';
4
+ import { FieldValues, UseFormProps } from 'react-hook-form';
5
5
  import { zodResolver } from '@hookform/resolvers/zod';
6
6
  import { z } from 'zod';
7
7
 
@@ -72,7 +72,7 @@ type PartialWithAllNullables<T> = {
72
72
  *
73
73
  * @param options - Configuration object
74
74
  * @param options.schema - Zod schema with two-type signature `z.ZodType<TOutput, TInput>`
75
- * @param options.defaultValues - Default form values (accepts nullable/undefined values)
75
+ * @param options.defaultValues - Default form values (shallow partial - nested objects must be complete if provided)
76
76
  * @param options.zodResolverOptions - Optional zodResolver configuration
77
77
  * @param options....formOptions - All other react-hook-form useForm options
78
78
  *
@@ -217,9 +217,9 @@ type PartialWithAllNullables<T> = {
217
217
  * @see https://zod.dev for Zod schema documentation
218
218
  * @since 0.1.0
219
219
  */
220
- declare const useZodForm: <TOutput extends FieldValues, TFormInput extends PartialWithAllNullables<TOutput> = PartialWithNullableObjects<TOutput>, TInput extends TFormInput = TFormInput>({ schema, zodResolverOptions, ...formOptions }: {
220
+ declare const useZodForm: <TOutput extends FieldValues, TFormInput extends PartialWithAllNullables<TOutput> = PartialWithNullableObjects<TOutput>, TInput extends TFormInput = TFormInput, TDefault extends TFormInput = TFormInput>({ schema, zodResolverOptions, ...formOptions }: {
221
221
  schema: z.ZodType<TOutput, TInput>;
222
- defaultValues?: DefaultValues<TFormInput>;
222
+ defaultValues?: TDefault;
223
223
  zodResolverOptions?: Parameters<typeof zodResolver>[1];
224
224
  } & Omit<UseFormProps<TFormInput, unknown, TOutput>, "resolver" | "defaultValues">) => react_hook_form.UseFormReturn<TFormInput, unknown, TOutput>;
225
225
 
package/dist/index.js CHANGED
@@ -5,6 +5,8 @@ var zod = require('@hookform/resolvers/zod');
5
5
  var reactHookForm = require('react-hook-form');
6
6
 
7
7
  var __defProp = Object.defineProperty;
8
+ var __defProps = Object.defineProperties;
9
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
8
10
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
11
  var __hasOwnProp = Object.prototype.hasOwnProperty;
10
12
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -20,6 +22,7 @@ var __spreadValues = (a, b) => {
20
22
  }
21
23
  return a;
22
24
  };
25
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
26
  var __objRest = (source, exclude) => {
24
27
  var target = {};
25
28
  for (var prop in source)
@@ -44,9 +47,12 @@ var useZodForm = (_a) => {
44
47
  schema,
45
48
  zodResolverOptions
46
49
  );
47
- return reactHookForm.useForm(__spreadValues({
50
+ const defaultValues = formOptions.defaultValues;
51
+ return reactHookForm.useForm(__spreadProps(__spreadValues({
48
52
  resolver
49
- }, formOptions));
53
+ }, formOptions), {
54
+ defaultValues
55
+ }));
50
56
  };
51
57
 
52
58
  exports.useZodForm = useZodForm;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
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"]}
1
+ {"version":3,"sources":["../src/use-zod-form.ts"],"names":["zodResolver","useForm"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiLO,IAAM,UAAA,GAAa,CAMxB,EAAA,KAWI;AAXJ,EAAA,IAAA,EAAA,GAAA,EAAA,EACA;AAAA,IAAA,MAAA;AAAA,IACA;AAAA,GAzLF,GAuLE,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;AAIA,EAAA,MAAM,gBAAgB,WAAA,CAAY,aAAA;AAElC,EAAA,OAAOC,qBAAA,CAAQ,aAAA,CAAA,cAAA,CAAA;AAAA,IACb;AAAA,GAAA,EACG,WAAA,CAAA,EAFU;AAAA,IAGb;AAAA,GACF,CAAC,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 (shallow partial - nested objects must be complete if provided)\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 TDefault extends TFormInput = TFormInput,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<TOutput, TInput>;\n defaultValues?: TDefault;\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 // can't help but to assert here - because DefaultValues<TDefault> makes object fields DeepPartial\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const defaultValues = formOptions.defaultValues as DefaultValues<TDefault>;\n\n return useForm({\n resolver,\n ...formOptions,\n defaultValues,\n });\n};\n"]}
package/dist/index.mjs CHANGED
@@ -3,6 +3,8 @@ import { zodResolver } from '@hookform/resolvers/zod';
3
3
  import { useForm } from 'react-hook-form';
4
4
 
5
5
  var __defProp = Object.defineProperty;
6
+ var __defProps = Object.defineProperties;
7
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
8
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
10
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -18,6 +20,7 @@ var __spreadValues = (a, b) => {
18
20
  }
19
21
  return a;
20
22
  };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
24
  var __objRest = (source, exclude) => {
22
25
  var target = {};
23
26
  for (var prop in source)
@@ -42,9 +45,12 @@ var useZodForm = (_a) => {
42
45
  schema,
43
46
  zodResolverOptions
44
47
  );
45
- return useForm(__spreadValues({
48
+ const defaultValues = formOptions.defaultValues;
49
+ return useForm(__spreadProps(__spreadValues({
46
50
  resolver
47
- }, formOptions));
51
+ }, formOptions), {
52
+ defaultValues
53
+ }));
48
54
  };
49
55
 
50
56
  export { useZodForm };
@@ -1 +1 @@
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"]}
1
+ {"version":3,"sources":["../src/use-zod-form.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiLO,IAAM,UAAA,GAAa,CAMxB,EAAA,KAWI;AAXJ,EAAA,IAAA,EAAA,GAAA,EAAA,EACA;AAAA,IAAA,MAAA;AAAA,IACA;AAAA,GAzLF,GAuLE,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;AAIA,EAAA,MAAM,gBAAgB,WAAA,CAAY,aAAA;AAElC,EAAA,OAAO,OAAA,CAAQ,aAAA,CAAA,cAAA,CAAA;AAAA,IACb;AAAA,GAAA,EACG,WAAA,CAAA,EAFU;AAAA,IAGb;AAAA,GACF,CAAC,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 (shallow partial - nested objects must be complete if provided)\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 TDefault extends TFormInput = TFormInput,\n>({\n schema,\n zodResolverOptions,\n ...formOptions\n}: {\n schema: z.ZodType<TOutput, TInput>;\n defaultValues?: TDefault;\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 // can't help but to assert here - because DefaultValues<TDefault> makes object fields DeepPartial\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const defaultValues = formOptions.defaultValues as DefaultValues<TDefault>;\n\n return useForm({\n resolver,\n ...formOptions,\n defaultValues,\n });\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zod-utils/react-hook-form",
3
- "version": "0.7.0",
3
+ "version": "0.8.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",