@tanstack/form-core 0.3.4 → 0.3.5

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/src/FieldApi.ts CHANGED
@@ -4,59 +4,67 @@ import { Store } from '@tanstack/store'
4
4
 
5
5
  export type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'
6
6
 
7
- type ValidateFn<TData, TFormData> = (
7
+ type ValidateFn<TData, TParentData, TName extends DeepKeys<TParentData>> = (
8
8
  value: TData,
9
- fieldApi: FieldApi<TData, TFormData>,
9
+ fieldApi: FieldApi<TData, TParentData, TName>,
10
10
  ) => ValidationError
11
11
 
12
- type ValidateAsyncFn<TData, TFormData> = (
12
+ type ValidateAsyncFn<
13
+ TData,
14
+ TParentData,
15
+ TName extends DeepKeys<TParentData>,
16
+ > = (
13
17
  value: TData,
14
- fieldApi: FieldApi<TData, TFormData>,
18
+ fieldApi: FieldApi<TData, TParentData, TName>,
15
19
  ) => ValidationError | Promise<ValidationError>
16
20
 
17
21
  export interface FieldOptions<
18
- _TData,
19
- TFormData,
22
+ TData,
23
+ TParentData,
20
24
  /**
21
25
  * This allows us to restrict the name to only be a valid field name while
22
26
  * also assigning it to a generic
23
27
  */
24
- TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
28
+ TName extends DeepKeys<TParentData>,
25
29
  /**
26
30
  * If TData is unknown, we can use the TName generic to determine the type
27
31
  */
28
- TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData,
32
+ TResolvedData = unknown extends TData ? DeepValue<TParentData, TName> : TData,
29
33
  > {
30
- name: TName
31
- index?: TData extends any[] ? number : never
32
- defaultValue?: TData
34
+ name: DeepKeys<TParentData>
35
+ index?: TResolvedData extends any[] ? number : never
36
+ defaultValue?: TResolvedData
33
37
  asyncDebounceMs?: number
34
38
  asyncAlways?: boolean
35
- onMount?: (formApi: FieldApi<TData, TFormData>) => void
36
- onChange?: ValidateFn<TData, TFormData>
37
- onChangeAsync?: ValidateAsyncFn<TData, TFormData>
39
+ onMount?: (formApi: FieldApi<TResolvedData, TParentData, TName>) => void
40
+ onChange?: ValidateFn<TResolvedData, TParentData, TName>
41
+ onChangeAsync?: ValidateAsyncFn<TResolvedData, TParentData, TName>
38
42
  onChangeAsyncDebounceMs?: number
39
- onBlur?: ValidateFn<TData, TFormData>
40
- onBlurAsync?: ValidateAsyncFn<TData, TFormData>
43
+ onBlur?: ValidateFn<TResolvedData, TParentData, TName>
44
+ onBlurAsync?: ValidateAsyncFn<TResolvedData, TParentData, TName>
41
45
  onBlurAsyncDebounceMs?: number
42
- onSubmitAsync?: ValidateAsyncFn<TData, TFormData>
46
+ onSubmitAsync?: ValidateAsyncFn<TResolvedData, TParentData, TName>
43
47
  defaultMeta?: Partial<FieldMeta>
44
48
  }
45
49
 
46
50
  export interface FieldApiOptions<
47
- _TData,
48
- TFormData,
51
+ TData,
52
+ TParentData,
49
53
  /**
50
54
  * This allows us to restrict the name to only be a valid field name while
51
55
  * also assigning it to a generic
52
56
  */
53
- TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
57
+ TName extends DeepKeys<TParentData>,
54
58
  /**
55
59
  * If TData is unknown, we can use the TName generic to determine the type
56
60
  */
57
- TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData,
58
- > extends FieldOptions<_TData, TFormData, TName, TData> {
59
- form: FormApi<TFormData>
61
+ TResolvedData extends ResolveData<TData, TParentData, TName> = ResolveData<
62
+ TData,
63
+ TParentData,
64
+ TName
65
+ >,
66
+ > extends FieldOptions<TData, TParentData, TName, TResolvedData> {
67
+ form: FormApi<TParentData>
60
68
  }
61
69
 
62
70
  export type FieldMeta = {
@@ -74,43 +82,35 @@ export type FieldState<TData> = {
74
82
  meta: FieldMeta
75
83
  }
76
84
 
77
- type GetTData<
78
- TData,
79
- TFormData,
80
- Opts extends FieldApiOptions<TData, TFormData>,
81
- > = Opts extends FieldApiOptions<
82
- infer _TData,
83
- infer _TFormData,
84
- infer _TName,
85
- infer RealTData
86
- >
87
- ? RealTData
88
- : never
85
+ export type ResolveData<TData, TParentData, TName> = unknown extends TData
86
+ ? DeepValue<TParentData, TName>
87
+ : TData
88
+
89
+ export type ResolveName<TParentData> = unknown extends TParentData
90
+ ? string
91
+ : DeepKeys<TParentData>
89
92
 
90
93
  export class FieldApi<
91
- _TData,
92
- TFormData,
93
- Opts extends FieldApiOptions<_TData, TFormData> = FieldApiOptions<
94
- _TData,
95
- TFormData
96
- >,
97
- TData extends GetTData<_TData, TFormData, Opts> = GetTData<
98
- _TData,
99
- TFormData,
100
- Opts
94
+ TData,
95
+ TParentData,
96
+ TName extends DeepKeys<TParentData>,
97
+ TResolvedData extends ResolveData<TData, TParentData, TName> = ResolveData<
98
+ TData,
99
+ TParentData,
100
+ TName
101
101
  >,
102
102
  > {
103
103
  uid: number
104
- form: Opts['form']
105
- name!: DeepKeys<TFormData>
106
- options: Opts = {} as any
107
- store!: Store<FieldState<TData>>
108
- state!: FieldState<TData>
109
- prevState!: FieldState<TData>
104
+ form: FieldApiOptions<TData, TParentData, TName, TResolvedData>['form']
105
+ name!: DeepKeys<TParentData>
106
+ options: FieldApiOptions<TData, TParentData, TName> = {} as any
107
+ store!: Store<FieldState<TResolvedData>>
108
+ state!: FieldState<TResolvedData>
109
+ prevState!: FieldState<TResolvedData>
110
110
 
111
111
  constructor(
112
- opts: Opts & {
113
- form: FormApi<TFormData>
112
+ opts: FieldApiOptions<TData, TParentData, TName, TResolvedData> & {
113
+ form: FormApi<TParentData>
114
114
  },
115
115
  ) {
116
116
  this.form = opts.form
@@ -123,7 +123,7 @@ export class FieldApi<
123
123
 
124
124
  this.name = opts.name as any
125
125
 
126
- this.store = new Store<FieldState<TData>>(
126
+ this.store = new Store<FieldState<TResolvedData>>(
127
127
  {
128
128
  value: this.getValue(),
129
129
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -190,12 +190,12 @@ export class FieldApi<
190
190
  }
191
191
  }
192
192
 
193
- update = (opts: FieldApiOptions<TData, TFormData>) => {
193
+ update = (opts: FieldApiOptions<TResolvedData, TParentData, TName>) => {
194
194
  // Default Value
195
195
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
196
196
  if (this.state.value === undefined) {
197
197
  const formDefault =
198
- opts.form.options.defaultValues?.[opts.name as keyof TFormData]
198
+ opts.form.options.defaultValues?.[opts.name as keyof TParentData]
199
199
 
200
200
  if (opts.defaultValue !== undefined) {
201
201
  this.setValue(opts.defaultValue as never)
@@ -212,12 +212,12 @@ export class FieldApi<
212
212
  this.options = opts as never
213
213
  }
214
214
 
215
- getValue = (): TData => {
216
- return this.form.getFieldValue(this.name)
215
+ getValue = (): TResolvedData => {
216
+ return this.form.getFieldValue(this.name) as any
217
217
  }
218
218
 
219
219
  setValue = (
220
- updater: Updater<TData>,
220
+ updater: Updater<TResolvedData>,
221
221
  options?: { touch?: boolean; notify?: boolean },
222
222
  ) => {
223
223
  this.form.setFieldValue(this.name, updater as never, options)
@@ -241,12 +241,13 @@ export class FieldApi<
241
241
 
242
242
  getInfo = () => this.form.getFieldInfo(this.name)
243
243
 
244
- pushValue = (value: TData extends any[] ? TData[number] : never) =>
245
- this.form.pushFieldValue(this.name, value as any)
244
+ pushValue = (
245
+ value: TResolvedData extends any[] ? TResolvedData[number] : never,
246
+ ) => this.form.pushFieldValue(this.name, value as any)
246
247
 
247
248
  insertValue = (
248
249
  index: number,
249
- value: TData extends any[] ? TData[number] : never,
250
+ value: TResolvedData extends any[] ? TResolvedData[number] : never,
250
251
  ) => this.form.insertFieldValue(this.name, index, value as any)
251
252
 
252
253
  removeValue = (index: number) => this.form.removeFieldValue(this.name, index)
@@ -254,11 +255,21 @@ export class FieldApi<
254
255
  swapValues = (aIndex: number, bIndex: number) =>
255
256
  this.form.swapFieldValues(this.name, aIndex, bIndex)
256
257
 
257
- getSubField = <TName extends DeepKeys<TData>>(name: TName) =>
258
- new FieldApi<DeepValue<TData, TName>, TFormData>({
258
+ getSubField = <
259
+ TSubData,
260
+ TSubName extends DeepKeys<TResolvedData>,
261
+ TSubResolvedData extends ResolveData<
262
+ DeepValue<TResolvedData, TSubName>,
263
+ TResolvedData,
264
+ TSubName
265
+ >,
266
+ >(
267
+ name: TSubName,
268
+ ): FieldApi<TSubData, TResolvedData, TSubName, TSubResolvedData> =>
269
+ new FieldApi({
259
270
  name: `${this.name}.${name}` as never,
260
271
  form: this.form,
261
- })
272
+ }) as any
262
273
 
263
274
  validateSync = (value = this.state.value, cause: ValidationCause) => {
264
275
  const { onChange, onBlur } = this.options
@@ -387,7 +398,7 @@ export class FieldApi<
387
398
 
388
399
  validate = (
389
400
  cause: ValidationCause,
390
- value?: TData,
401
+ value?: TResolvedData,
391
402
  ): ValidationError[] | Promise<ValidationError[]> => {
392
403
  // If the field is pristine and validatePristine is false, do not validate
393
404
  if (!this.state.meta.isTouched) return []
@@ -405,7 +416,7 @@ export class FieldApi<
405
416
  return this.validateAsync(value, cause)
406
417
  }
407
418
 
408
- handleChange = (updater: Updater<TData>) => {
419
+ handleChange = (updater: Updater<TResolvedData>) => {
409
420
  this.setValue(updater, { touch: true })
410
421
  }
411
422
 
package/src/FormApi.ts CHANGED
@@ -31,7 +31,7 @@ export type FormOptions<TData> = {
31
31
  }
32
32
 
33
33
  export type FieldInfo<TFormData> = {
34
- instances: Record<string, FieldApi<any, TFormData>>
34
+ instances: Record<string, FieldApi<any, TFormData, any>>
35
35
  } & ValidationMeta
36
36
 
37
37
  export type ValidationMeta = {
@@ -106,7 +106,7 @@ export class FormApi<TFormData> {
106
106
  constructor(opts?: FormOptions<TFormData>) {
107
107
  this.store = new Store<FormState<TFormData>>(
108
108
  getDefaultFormState({
109
- ...opts?.defaultState,
109
+ ...(opts?.defaultState as any),
110
110
  values: opts?.defaultValues ?? opts?.defaultState?.values,
111
111
  isFormValid: true,
112
112
  }),
@@ -174,7 +174,7 @@ export class FormApi<TFormData> {
174
174
  getDefaultFormState(
175
175
  Object.assign(
176
176
  {},
177
- this.state,
177
+ this.state as any,
178
178
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
179
179
  shouldUpdateState ? options.defaultState : {},
180
180
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -194,7 +194,7 @@ export class FormApi<TFormData> {
194
194
  reset = () =>
195
195
  this.store.setState(() =>
196
196
  getDefaultFormState({
197
- ...this.options.defaultState,
197
+ ...(this.options.defaultState as any),
198
198
  values: this.options.defaultValues ?? this.options.defaultState?.values,
199
199
  }),
200
200
  )
@@ -288,7 +288,9 @@ export class FormApi<TFormData> {
288
288
  return this.state.fieldMeta[field]
289
289
  }
290
290
 
291
- getFieldInfo = <TField extends DeepKeys<TFormData>>(field: TField) => {
291
+ getFieldInfo = <TField extends DeepKeys<TFormData>>(
292
+ field: TField,
293
+ ): FieldInfo<TFormData> => {
292
294
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
293
295
  return (this.fieldInfo[field] ||= {
294
296
  instances: {},
package/src/utils.ts CHANGED
@@ -133,22 +133,28 @@ type AllowedIndexes<
133
133
  ? AllowedIndexes<Tail, Keys | Tail['length']>
134
134
  : Keys
135
135
 
136
- export type DeepKeys<T> = unknown extends T
137
- ? keyof T
136
+ export type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5
137
+ ? never
138
+ : unknown extends T
139
+ ? string
138
140
  : object extends T
139
141
  ? string
140
142
  : T extends readonly any[] & IsTuple<T>
141
- ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>>
143
+ ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>, TDepth>
142
144
  : T extends any[]
143
- ? DeepKeys<T[number]>
145
+ ? DeepKeys<T[number], [...TDepth, any]>
144
146
  : T extends Date
145
147
  ? never
146
148
  : T extends object
147
- ? (keyof T & string) | DeepKeysPrefix<T, keyof T>
149
+ ? (keyof T & string) | DeepKeysPrefix<T, keyof T, TDepth>
148
150
  : never
149
151
 
150
- type DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string)
151
- ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}`
152
+ type DeepKeysPrefix<
153
+ T,
154
+ TPrefix,
155
+ TDepth extends any[],
156
+ > = TPrefix extends keyof T & (number | string)
157
+ ? `${TPrefix}.${DeepKeys<T[TPrefix], [...TDepth, any]> & string}`
152
158
  : never
153
159
 
154
160
  export type DeepValue<T, TProp> = T extends Record<string | number, any>