Skip to content

Commit 145e0a9

Browse files
authored
fix: Remove form-core type overrides from adapters (#794)
* chore: remove core overrides in React adapter * chore: remove module declarating types from solid form * chore: remove module declarating types from vue form * chore: fix prettier, knip
1 parent f53062f commit 145e0a9

File tree

7 files changed

+178
-190
lines changed

7 files changed

+178
-190
lines changed

packages/react-form/src/createServerValidate.ts

+7-12
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,11 @@ type OnServerValidateOrFn<
1717
? FFN | OnServerValidateFn<TFormData>
1818
: OnServerValidateFn<TFormData>
1919

20-
declare module '@tanstack/form-core' {
21-
// eslint-disable-next-line no-shadow
22-
interface FormOptions<
23-
TFormData,
24-
TFormValidator extends
25-
| Validator<TFormData, unknown>
26-
| undefined = undefined,
27-
> {
28-
onServerValidate?: OnServerValidateOrFn<TFormData, TFormValidator>
29-
}
20+
interface ServerFormOptions<
21+
TFormData,
22+
TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
23+
> extends FormOptions<TFormData, TFormValidator> {
24+
onServerValidate?: OnServerValidateOrFn<TFormData, TFormValidator>
3025
}
3126

3227
type ValidateFormData<
@@ -41,13 +36,13 @@ export const createServerValidate = <
4136
TFormData,
4237
TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
4338
>(
44-
defaultOpts?: FormOptions<TFormData, TFormValidator>,
39+
defaultOpts: ServerFormOptions<TFormData, TFormValidator>,
4540
) =>
4641
(async (
4742
formData: FormData,
4843
info?: Parameters<typeof decode>[1],
4944
): Promise<Partial<FormApi<TFormData, TFormValidator>['state']>> => {
50-
const { validatorAdapter, onServerValidate } = defaultOpts || {}
45+
const { validatorAdapter, onServerValidate } = defaultOpts
5146

5247
const runValidator = (propsValue: { value: TFormData }) => {
5348
if (validatorAdapter && typeof onServerValidate !== 'function') {

packages/react-form/src/useField.tsx

+15-23
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,16 @@ import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
55
import type { NodeType, UseFieldOptions } from './types'
66
import type { DeepKeys, DeepValue, Validator } from '@tanstack/form-core'
77

8-
declare module '@tanstack/form-core' {
8+
interface ReactFieldApi<
9+
TParentData,
10+
TFormValidator extends
11+
| Validator<TParentData, unknown>
12+
| undefined = undefined,
13+
> {
914
/**
10-
* When using `@tanstack/react-form`, the core field API is extended at type level with additional methods for React-specific functionality:
15+
* A pre-bound and type-safe sub-field component using this field as a root.
1116
*/
12-
// eslint-disable-next-line no-shadow
13-
interface FieldApi<
14-
TParentData,
15-
TName extends DeepKeys<TParentData>,
16-
TFieldValidator extends
17-
| Validator<DeepValue<TParentData, TName>, unknown>
18-
| undefined = undefined,
19-
TFormValidator extends
20-
| Validator<TParentData, unknown>
21-
| undefined = undefined,
22-
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
23-
> {
24-
/**
25-
* A pre-bound and type-safe sub-field component using this field as a root.
26-
*/
27-
Field: FieldComponent<TParentData, TFormValidator>
28-
}
17+
Field: FieldComponent<TParentData, TFormValidator>
2918
}
3019

3120
/**
@@ -75,17 +64,20 @@ export function useField<
7564
TFormValidator,
7665
TData
7766
>,
78-
): FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData> {
67+
) {
7968
const [fieldApi] = useState(() => {
8069
const api = new FieldApi({
8170
...opts,
8271
form: opts.form,
8372
name: opts.name,
8473
})
8574

86-
api.Field = Field as never
75+
const extendedApi: typeof api & ReactFieldApi<TParentData, TFormValidator> =
76+
api as never
77+
78+
extendedApi.Field = Field as never
8779

88-
return api
80+
return extendedApi
8981
})
9082

9183
useIsomorphicLayoutEffect(fieldApi.mount, [fieldApi])
@@ -107,7 +99,7 @@ export function useField<
10799
: undefined,
108100
)
109101

110-
return fieldApi as never
102+
return fieldApi
111103
}
112104

113105
/**

packages/react-form/src/useForm.tsx

+37-36
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,32 @@ import type { NoInfer } from '@tanstack/react-store'
77
import type { FormOptions, FormState, Validator } from '@tanstack/form-core'
88
import type { NodeType } from './types'
99

10-
declare module '@tanstack/form-core' {
10+
/**
11+
* Fields that are added onto the `FormAPI` from `@tanstack/form-core` and returned from `useForm`
12+
*/
13+
interface ReactFormApi<
14+
TFormData,
15+
TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
16+
> {
1117
/**
12-
* When using `@tanstack/react-form`, the core form API is extended at type level with additional methods for React-specific functionality:
18+
* A React component to render form fields. With this, you can render and manage individual form fields.
1319
*/
14-
// eslint-disable-next-line no-shadow
15-
interface FormApi<TFormData, TFormValidator> {
16-
/**
17-
* A React component to render form fields. With this, you can render and manage individual form fields.
18-
*/
19-
Field: FieldComponent<TFormData, TFormValidator>
20-
/**
21-
* A custom React hook that provides functionalities related to individual form fields. It gives you access to field values, errors, and allows you to set or update field values.
22-
*/
23-
useField: UseField<TFormData, TFormValidator>
24-
/**
25-
* A `useStore` hook that connects to the internal store of the form. It can be used to access the form's current state or any other related state information. You can optionally pass in a selector function to cherry-pick specific parts of the state
26-
*/
27-
useStore: <TSelected = NoInfer<FormState<TFormData>>>(
28-
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,
29-
) => TSelected
20+
Field: FieldComponent<TFormData, TFormValidator>
21+
/**
22+
* A custom React hook that provides functionalities related to individual form fields. It gives you access to field values, errors, and allows you to set or update field values.
23+
*/
24+
useField: UseField<TFormData, TFormValidator>
25+
/**
26+
* A `useStore` hook that connects to the internal store of the form. It can be used to access the form's current state or any other related state information. You can optionally pass in a selector function to cherry-pick specific parts of the state
27+
*/
28+
useStore: <TSelected = NoInfer<FormState<TFormData>>>(
29+
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,
30+
) => TSelected
31+
/**
32+
* A `Subscribe` function that allows you to listen and react to changes in the form's state. It's especially useful when you need to execute side effects or render specific components in response to state updates.
33+
*/
34+
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
3035
/**
31-
* A `Subscribe` function that allows you to listen and react to changes in the form's state. It's especially useful when you need to execute side effects or render specific components in response to state updates.
32-
*/
33-
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
34-
/**
3536
TypeScript versions <=5.0.4 have a bug that prevents
3637
the type of the `TSelected` generic from being inferred
3738
from the return type of this method.
@@ -42,43 +43,43 @@ declare module '@tanstack/form-core' {
4243
@see {@link https://github.com/TanStack/form/pull/606/files#r1506715714 | This discussion on GitHub for the details}
4344
@see {@link https://github.com/microsoft/TypeScript/issues/52786 | The bug report in `microsoft/TypeScript`}
4445
*/
45-
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
46-
children: ((state: NoInfer<TSelected>) => NodeType) | NodeType
47-
}) => NodeType
48-
}
46+
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
47+
children: ((state: NoInfer<TSelected>) => NodeType) | NodeType
48+
}) => NodeType
4949
}
5050

5151
/**
52-
* A custom React Hook that returns an instance of the `FormApi` class.
52+
* A custom React Hook that returns an extended instance of the `FormApi` class.
5353
*
5454
* This API encapsulates all the necessary functionalities related to the form. It allows you to manage form state, handle submissions, and interact with form fields
5555
*/
5656
export function useForm<
5757
TFormData,
5858
TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
59-
>(
60-
opts?: FormOptions<TFormData, TFormValidator>,
61-
): FormApi<TFormData, TFormValidator> {
59+
>(opts?: FormOptions<TFormData, TFormValidator>) {
6260
const [formApi] = useState(() => {
6361
const api = new FormApi<TFormData, TFormValidator>(opts)
64-
api.Field = function APIField(props) {
62+
63+
const extendedApi: typeof api & ReactFormApi<TFormData, TFormValidator> =
64+
api as never
65+
extendedApi.Field = function APIField(props) {
6566
return (<Field {...props} form={api} />) as never
6667
}
6768
// eslint-disable-next-line react-hooks/rules-of-hooks
68-
api.useField = (props) => useField({ ...props, form: api })
69-
api.useStore = (selector) => {
69+
extendedApi.useField = (props) => useField({ ...props, form: api })
70+
extendedApi.useStore = (selector) => {
7071
// eslint-disable-next-line react-hooks/rules-of-hooks
7172
return useStore(api.store as any, selector as any) as any
7273
}
73-
api.Subscribe = (props) => {
74+
extendedApi.Subscribe = (props) => {
7475
return functionalUpdate(
7576
props.children,
7677
// eslint-disable-next-line react-hooks/rules-of-hooks
7778
useStore(api.store as any, props.selector as any),
7879
) as any
7980
}
8081

81-
return api
82+
return extendedApi
8283
})
8384

8485
useIsomorphicLayoutEffect(formApi.mount, [])
@@ -93,5 +94,5 @@ export function useForm<
9394
formApi.update(opts)
9495
})
9596

96-
return formApi as any
97+
return formApi
9798
}

packages/solid-form/src/createField.tsx

+44-25
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,13 @@ import type {
1717
import type { JSXElement } from 'solid-js'
1818
import type { CreateFieldOptions } from './types'
1919

20-
declare module '@tanstack/form-core' {
21-
// eslint-disable-next-line no-shadow
22-
interface FieldApi<
23-
TParentData,
24-
TName extends DeepKeys<TParentData>,
25-
TFieldValidator extends
26-
| Validator<DeepValue<TParentData, TName>, unknown>
27-
| undefined = undefined,
28-
TFormValidator extends
29-
| Validator<TParentData, unknown>
30-
| undefined = undefined,
31-
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
32-
> {
33-
Field: FieldComponent<TParentData, TFormValidator>
34-
}
20+
interface SolidFieldApi<
21+
TParentData,
22+
TFormValidator extends
23+
| Validator<TParentData, unknown>
24+
| undefined = undefined,
25+
> {
26+
Field: FieldComponent<TParentData, TFormValidator>
3527
}
3628

3729
export type CreateField<
@@ -56,12 +48,35 @@ export type CreateField<
5648
>,
5749
'form'
5850
>,
59-
) => () => FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>
51+
) => () => FieldApi<
52+
TParentData,
53+
TName,
54+
TFieldValidator,
55+
TFormValidator,
56+
TData
57+
> &
58+
SolidFieldApi<TParentData, TFormValidator>
6059

6160
// ugly way to trick solid into triggering updates for changes on the fieldApi
62-
function makeFieldReactive<FieldApiT extends FieldApi<any, any, any, any>>(
63-
fieldApi: FieldApiT,
64-
): () => FieldApiT {
61+
function makeFieldReactive<
62+
TParentData,
63+
TName extends DeepKeys<TParentData>,
64+
TFieldValidator extends
65+
| Validator<DeepValue<TParentData, TName>, unknown>
66+
| undefined = undefined,
67+
TFormValidator extends
68+
| Validator<TParentData, unknown>
69+
| undefined = undefined,
70+
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
71+
FieldApiT extends FieldApi<
72+
TParentData,
73+
TName,
74+
TFieldValidator,
75+
TFormValidator,
76+
TData
77+
> = FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData> &
78+
SolidFieldApi<TParentData, TFormValidator>,
79+
>(fieldApi: FieldApiT): () => FieldApiT {
6580
const [flag, setFlag] = createSignal(false)
6681
const fieldApiMemo = createMemo(() => [flag(), fieldApi] as const)
6782
const unsubscribeStore = fieldApi.store.subscribe(() => setFlag((f) => !f))
@@ -87,24 +102,28 @@ export function createField<
87102
TFormValidator,
88103
TData
89104
>,
90-
): () => FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData> {
105+
) {
91106
const options = opts()
92107

93-
const fieldApi = new FieldApi(options)
94-
fieldApi.Field = Field as never
108+
const api = new FieldApi(options)
109+
110+
const extendedApi: typeof api & SolidFieldApi<TParentData, TFormValidator> =
111+
api as never
112+
113+
extendedApi.Field = Field as never
95114

96115
/**
97116
* fieldApi.update should not have any side effects. Think of it like a `useRef`
98117
* that we need to keep updated every render with the most up-to-date information.
99118
*
100119
* createComputed to make sure this effect runs before render effects
101120
*/
102-
createComputed(() => fieldApi.update(opts()))
121+
createComputed(() => api.update(opts()))
103122

104123
// Instantiates field meta and removes it when unrendered
105-
onMount(() => onCleanup(fieldApi.mount()))
124+
onMount(() => onCleanup(api.mount()))
106125

107-
return makeFieldReactive(fieldApi) as never
126+
return makeFieldReactive(extendedApi as never)
108127
}
109128

110129
type FieldComponentProps<

0 commit comments

Comments
 (0)