Skip to content

Commit 7523369

Browse files
authored
Merge pull request #165 from dkAndFed/fix/TS-types
fix: TS types
2 parents 42e348c + b66a123 commit 7523369

File tree

4 files changed

+103
-45
lines changed

4 files changed

+103
-45
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "remix-hook-form",
3-
"version": "7.0.0",
3+
"version": "7.0.1",
44
"description": "Utility wrapper around react-hook-form for use with react-router v7+",
55
"type": "module",
66
"main": "./dist/index.cjs",

src/hook/index.tsx

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,15 @@ import { createFormData } from "../utilities";
3737

3838
export type SubmitFunctionOptions = Parameters<SubmitFunction>[1];
3939

40-
export interface UseRemixFormOptions<T extends FieldValues>
41-
extends UseFormProps<T> {
40+
export interface UseRemixFormOptions<
41+
TFieldValues extends FieldValues,
42+
// biome-ignore lint/suspicious/noExplicitAny: defaults to any type
43+
TContext = any,
44+
TTransformedValues = TFieldValues,
45+
> extends UseFormProps<TFieldValues, TContext, TTransformedValues> {
4246
submitHandlers?: {
43-
onValid?: SubmitHandler<T>;
44-
onInvalid?: SubmitErrorHandler<T>;
47+
onValid?: SubmitHandler<TTransformedValues>;
48+
onInvalid?: SubmitErrorHandler<TFieldValues>;
4549
};
4650
submitConfig?: SubmitFunctionOptions;
4751
submitData?: FieldValues;
@@ -51,23 +55,27 @@ export interface UseRemixFormOptions<T extends FieldValues>
5155
*/
5256
stringifyAllValues?: boolean;
5357
}
54-
55-
export const useRemixForm = <T extends FieldValues>({
58+
export const useRemixForm = <
59+
TFieldValues extends FieldValues,
60+
// biome-ignore lint/suspicious/noExplicitAny: defaults to any type
61+
TContext = any,
62+
TTransformedValues = TFieldValues,
63+
>({
5664
submitHandlers,
5765
submitConfig,
5866
submitData,
5967
fetcher,
6068
stringifyAllValues = true,
6169
...formProps
62-
}: UseRemixFormOptions<T>) => {
70+
}: UseRemixFormOptions<TFieldValues, TContext, TTransformedValues>) => {
6371
const [isSubmittedSuccessfully, setIsSubmittedSuccessfully] = useState(false);
6472
const basename = useHref("/");
6573
const actionSubmit = useSubmit();
6674
const actionData = useActionData();
6775
const submit = fetcher?.submit ?? actionSubmit;
6876
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
6977
const data: any = fetcher?.data ?? actionData;
70-
const methods = useForm<T>({ ...formProps, errors: data?.errors });
78+
const methods = useForm({ ...formProps, errors: data?.errors });
7179
const navigation = useNavigation();
7280
// Either it's submitted to an action or submitted to a fetcher (or neither)
7381
const isSubmittingForm = useMemo(
@@ -92,7 +100,7 @@ export const useRemixForm = <T extends FieldValues>({
92100
const onSubmit = useMemo(
93101
() =>
94102
(
95-
data: T,
103+
data: TTransformedValues,
96104
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
97105
e: any,
98106
formEncType?: FormEncType,
@@ -125,7 +133,7 @@ export const useRemixForm = <T extends FieldValues>({
125133

126134
// React-hook-form uses lazy property getters to avoid re-rendering when properties
127135
// that aren't being used change. Using getters here preservers that lazy behavior.
128-
const formState: FormState<T> = useMemo(
136+
const formState: FormState<TFieldValues> = useMemo(
129137
() => ({
130138
get isDirty() {
131139
return methods.formState.isDirty;
@@ -175,7 +183,7 @@ export const useRemixForm = <T extends FieldValues>({
175183
const reset = useMemo(
176184
() =>
177185
(
178-
values?: T | DefaultValues<T> | undefined,
186+
values?: TFieldValues | DefaultValues<TFieldValues> | undefined,
179187
options?: KeepStateOptions,
180188
) => {
181189
setIsSubmittedSuccessfully(false);
@@ -187,8 +195,8 @@ export const useRemixForm = <T extends FieldValues>({
187195
const register = useMemo(
188196
() =>
189197
(
190-
name: Path<T>,
191-
options?: RegisterOptions<T> & {
198+
name: Path<TFieldValues>,
199+
options?: RegisterOptions<TFieldValues> & {
192200
disableProgressiveEnhancement?: boolean;
193201
},
194202
) => {
@@ -241,15 +249,25 @@ export const useRemixForm = <T extends FieldValues>({
241249

242250
return hookReturn;
243251
};
244-
245-
export type UseRemixFormReturn<T extends FieldValues> = UseFormReturn<T> & {
252+
export type UseRemixFormReturn<
253+
TFieldValues extends FieldValues = FieldValues,
254+
// biome-ignore lint/suspicious/noExplicitAny: defaults to any type
255+
TContext = any,
256+
TTransformedValues = TFieldValues,
257+
> = UseFormReturn<TFieldValues, TContext, TTransformedValues> & {
246258
handleSubmit: ReturnType<typeof useRemixForm>["handleSubmit"];
247259
reset: ReturnType<typeof useRemixForm>["reset"];
248260
register: ReturnType<typeof useRemixForm>["register"];
249261
};
250-
251-
interface RemixFormProviderProps<T extends FieldValues>
252-
extends Omit<UseFormReturn<T>, "handleSubmit" | "reset"> {
262+
interface RemixFormProviderProps<
263+
TFieldValues extends FieldValues = FieldValues,
264+
// biome-ignore lint/suspicious/noExplicitAny: defaults to any type
265+
TContext = any,
266+
TTransformedValues = TFieldValues,
267+
> extends Omit<
268+
UseFormReturn<TFieldValues, TContext, TTransformedValues>,
269+
"handleSubmit" | "reset"
270+
> {
253271
children: ReactNode;
254272
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
255273
handleSubmit: any;
@@ -258,20 +276,29 @@ interface RemixFormProviderProps<T extends FieldValues>
258276
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
259277
reset: any;
260278
}
261-
export const RemixFormProvider = <T extends FieldValues>({
279+
export const RemixFormProvider = <
280+
TFieldValues extends FieldValues = FieldValues,
281+
// biome-ignore lint/suspicious/noExplicitAny: defaults to any type
282+
TContext = any,
283+
TTransformedValues = TFieldValues,
284+
>({
262285
children,
263286
...props
264-
}: RemixFormProviderProps<T>) => {
287+
}: RemixFormProviderProps<TFieldValues, TContext, TTransformedValues>) => {
265288
return <FormProvider {...props}>{children}</FormProvider>;
266289
};
267-
268-
export const useRemixFormContext = <T extends FieldValues>() => {
269-
const methods = useFormContext<T>();
290+
export const useRemixFormContext = <
291+
TFieldValues extends FieldValues,
292+
// biome-ignore lint/suspicious/noExplicitAny: defaults to any type
293+
TContext = any,
294+
TTransformedValues = TFieldValues,
295+
>() => {
296+
const methods = useFormContext<TFieldValues, TContext, TTransformedValues>();
270297
return {
271298
...methods,
272299
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
273300
handleSubmit: methods.handleSubmit as any as ReturnType<
274-
UseFormHandleSubmit<T>
301+
UseFormHandleSubmit<TFieldValues, TTransformedValues>
275302
>,
276303
};
277304
};

src/middleware/index.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,21 @@ export function unstable_extractFormDataMiddleware({
3636
export const getFormData = (context: unstable_RouterContextProvider) =>
3737
context.get(formDataContext);
3838

39-
export const getValidatedFormData = async <T extends FieldValues>(
39+
export const getValidatedFormData = async <
40+
TFieldValues extends FieldValues,
41+
// biome-ignore lint/suspicious/noExplicitAny: defaults to any type
42+
TContext = any,
43+
TTransformedValues = TFieldValues,
44+
>(
4045
context: unstable_RouterContextProvider,
41-
resolver: Resolver<T>,
46+
resolver: Resolver<TFieldValues, TContext, TTransformedValues>,
4247
) => {
4348
const formData = context.get(formDataContext);
44-
const data = await validateFormData(formData, resolver);
49+
const data = await validateFormData<
50+
TFieldValues,
51+
TContext,
52+
TTransformedValues
53+
>(formData, resolver);
4554
/* if (errors) {
4655
throw dataFn(
4756
{ errors, receivedValues: formData },

src/utilities/index.ts

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,42 @@ export const getFormDataFromSearchParams = <T extends FieldValues>(
9090
export const isGet = (request: Pick<Request, "method">) =>
9191
request.method === "GET" || request.method === "get";
9292

93-
type ReturnData<T extends FieldValues> =
94-
| { data: T; errors: undefined; receivedValues: Partial<T> }
95-
| { data: undefined; errors: FieldErrors<T>; receivedValues: Partial<T> };
93+
type ReturnData<
94+
TFieldValues extends FieldValues,
95+
TTransformedValues = TFieldValues,
96+
> =
97+
| {
98+
data: TTransformedValues;
99+
errors: undefined;
100+
receivedValues: Partial<TFieldValues>;
101+
}
102+
| {
103+
data: undefined;
104+
errors: FieldErrors<TFieldValues>;
105+
receivedValues: Partial<TFieldValues>;
106+
};
96107
/**
97108
* Parses the data from an HTTP request and validates it against a schema. Works in both loaders and actions, in loaders it extracts the data from the search params.
98109
* In actions it extracts it from request formData.
99110
*
100111
* @returns A Promise that resolves to an object containing the validated data or any errors that occurred during validation.
101112
*/
102-
export const getValidatedFormData = async <T extends FieldValues>(
113+
export const getValidatedFormData = async <
114+
TFieldValues extends FieldValues,
115+
// biome-ignore lint/suspicious/noExplicitAny: any by default
116+
TContext = any,
117+
TTransformedValues = TFieldValues,
118+
>(
103119
request: Request | FormData,
104-
resolver: Resolver<T>,
120+
resolver: Resolver<TFieldValues, TContext, TTransformedValues>,
105121
preserveStringified = false,
106-
): Promise<ReturnData<T>> => {
107-
const { receivedValues } = await getFormData<T>(request, preserveStringified);
122+
): Promise<ReturnData<TFieldValues, TTransformedValues>> => {
123+
const { receivedValues } = await getFormData<TFieldValues>(
124+
request,
125+
preserveStringified,
126+
);
108127

109-
const data = await validateFormData<T>(receivedValues, resolver);
128+
const data = await validateFormData(receivedValues, resolver);
110129

111130
return { ...data, receivedValues };
112131
};
@@ -134,24 +153,27 @@ export const getFormData = async <T extends FieldValues>(
134153
* @param resolver Schema to validate and cast the data with
135154
* @returns Returns the validated data if successful, otherwise returns the error object
136155
*/
137-
export const validateFormData = async <T extends FieldValues>(
156+
export const validateFormData = async <
157+
TFieldValues extends FieldValues,
158+
TContext,
159+
TTransformedValues = TFieldValues,
160+
>(
138161
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
139162
data: any,
140-
resolver: Resolver<T>,
163+
resolver: Resolver<TFieldValues, TContext, TTransformedValues>,
141164
) => {
142165
const dataToValidate =
143166
data instanceof FormData ? Object.fromEntries(data) : data;
144-
const { errors, values } = await resolver(
145-
dataToValidate,
146-
{},
147-
{ shouldUseNativeValidation: false, fields: {} },
148-
);
167+
const { errors, values } = await resolver(dataToValidate, {} as TContext, {
168+
shouldUseNativeValidation: false,
169+
fields: {},
170+
});
149171

150172
if (Object.keys(errors).length > 0) {
151-
return { errors: errors as FieldErrors<T>, data: undefined };
173+
return { errors: errors as FieldErrors<TFieldValues>, data: undefined };
152174
}
153175

154-
return { errors: undefined, data: values as T };
176+
return { errors: undefined, data: values as TTransformedValues };
155177
};
156178
/**
157179
Creates a new instance of FormData with the specified data and key.

0 commit comments

Comments
 (0)