@tanstack/form-core 0.13.7 → 0.16.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/dist/cjs/FieldApi.cjs +5 -4
- package/dist/cjs/FieldApi.cjs.map +1 -1
- package/dist/cjs/FieldApi.d.cts +4 -2
- package/dist/cjs/FormApi.cjs +9 -2
- package/dist/cjs/FormApi.cjs.map +1 -1
- package/dist/cjs/FormApi.d.cts +4 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/util-types.d.cts +29 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +0 -19
- package/dist/esm/FieldApi.d.ts +4 -2
- package/dist/esm/FieldApi.js +5 -4
- package/dist/esm/FieldApi.js.map +1 -1
- package/dist/esm/FormApi.d.ts +4 -1
- package/dist/esm/FormApi.js +9 -2
- package/dist/esm/FormApi.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/util-types.d.ts +29 -0
- package/dist/esm/utils.d.ts +0 -19
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/FieldApi.ts +12 -18
- package/src/FormApi.ts +14 -1
- package/src/index.ts +1 -0
- package/src/tests/FieldApi.spec.ts +7 -23
- package/src/tests/FieldApi.test-d.ts +1 -1
- package/src/tests/FormApi.spec.ts +48 -4
- package/src/tests/mutateMergeDeep.spec.ts +1 -1
- package/src/tests/util-types.test-d.ts +106 -0
- package/src/tests/utils.spec.ts +8 -8
- package/src/util-types.ts +99 -0
- package/src/utils.ts +1 -73
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect,
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
3
|
import { FormApi } from '../FormApi'
|
|
4
4
|
import { FieldApi } from '../FieldApi'
|
|
@@ -46,6 +46,8 @@ describe('field api', () => {
|
|
|
46
46
|
expect(field.getMeta()).toEqual({
|
|
47
47
|
isTouched: false,
|
|
48
48
|
isValidating: false,
|
|
49
|
+
isPristine: true,
|
|
50
|
+
isDirty: false,
|
|
49
51
|
touchedErrors: [],
|
|
50
52
|
errors: [],
|
|
51
53
|
errorMap: {},
|
|
@@ -57,12 +59,14 @@ describe('field api', () => {
|
|
|
57
59
|
const field = new FieldApi({
|
|
58
60
|
form,
|
|
59
61
|
name: 'name',
|
|
60
|
-
defaultMeta: { isTouched: true },
|
|
62
|
+
defaultMeta: { isTouched: true, isDirty: true, isPristine: false },
|
|
61
63
|
})
|
|
62
64
|
|
|
63
65
|
expect(field.getMeta()).toEqual({
|
|
64
66
|
isTouched: true,
|
|
65
67
|
isValidating: false,
|
|
68
|
+
isDirty: true,
|
|
69
|
+
isPristine: false,
|
|
66
70
|
touchedErrors: [],
|
|
67
71
|
errors: [],
|
|
68
72
|
errorMap: {},
|
|
@@ -154,26 +158,6 @@ describe('field api', () => {
|
|
|
154
158
|
expect(field.getValue()).toStrictEqual(['two', 'one'])
|
|
155
159
|
})
|
|
156
160
|
|
|
157
|
-
it('should get a subfield properly', () => {
|
|
158
|
-
const form = new FormApi({
|
|
159
|
-
defaultValues: {
|
|
160
|
-
names: {
|
|
161
|
-
first: 'one',
|
|
162
|
-
second: 'two',
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
const field = new FieldApi({
|
|
168
|
-
form,
|
|
169
|
-
name: 'names',
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
const subfield = field.getSubField('first')
|
|
173
|
-
|
|
174
|
-
expect(subfield.getValue()).toBe('one')
|
|
175
|
-
})
|
|
176
|
-
|
|
177
161
|
it('should not throw errors when no meta info is stored on a field and a form re-renders', async () => {
|
|
178
162
|
const form = new FormApi({
|
|
179
163
|
defaultValues: {
|
|
@@ -619,7 +603,7 @@ describe('field api', () => {
|
|
|
619
603
|
})
|
|
620
604
|
|
|
621
605
|
const unmount = field.mount()
|
|
622
|
-
const callback =
|
|
606
|
+
const callback = vi.fn()
|
|
623
607
|
const subscription = form.store.subscribe(callback)
|
|
624
608
|
unmount()
|
|
625
609
|
const info = form.getFieldInfo(field.name)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect } from 'vitest'
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
3
|
import { FormApi } from '../FormApi'
|
|
4
4
|
import { FieldApi } from '../FieldApi'
|
|
@@ -21,6 +21,8 @@ describe('form api', () => {
|
|
|
21
21
|
errorMap: {},
|
|
22
22
|
isSubmitting: false,
|
|
23
23
|
isTouched: false,
|
|
24
|
+
isPristine: true,
|
|
25
|
+
isDirty: false,
|
|
24
26
|
isValid: true,
|
|
25
27
|
isValidating: false,
|
|
26
28
|
submissionAttempts: 0,
|
|
@@ -55,6 +57,8 @@ describe('form api', () => {
|
|
|
55
57
|
isSubmitted: false,
|
|
56
58
|
isSubmitting: false,
|
|
57
59
|
isTouched: false,
|
|
60
|
+
isPristine: true,
|
|
61
|
+
isDirty: false,
|
|
58
62
|
isValid: true,
|
|
59
63
|
isValidating: false,
|
|
60
64
|
submissionAttempts: 0,
|
|
@@ -87,6 +91,8 @@ describe('form api', () => {
|
|
|
87
91
|
isSubmitted: false,
|
|
88
92
|
isSubmitting: false,
|
|
89
93
|
isTouched: false,
|
|
94
|
+
isPristine: true,
|
|
95
|
+
isDirty: false,
|
|
90
96
|
isValid: true,
|
|
91
97
|
isValidating: false,
|
|
92
98
|
submissionAttempts: 30,
|
|
@@ -130,6 +136,8 @@ describe('form api', () => {
|
|
|
130
136
|
isSubmitted: false,
|
|
131
137
|
isSubmitting: false,
|
|
132
138
|
isTouched: false,
|
|
139
|
+
isPristine: true,
|
|
140
|
+
isDirty: false,
|
|
133
141
|
isValid: true,
|
|
134
142
|
isValidating: false,
|
|
135
143
|
submissionAttempts: 300,
|
|
@@ -170,6 +178,8 @@ describe('form api', () => {
|
|
|
170
178
|
isSubmitted: false,
|
|
171
179
|
isSubmitting: false,
|
|
172
180
|
isTouched: false,
|
|
181
|
+
isPristine: true,
|
|
182
|
+
isDirty: false,
|
|
173
183
|
isValid: true,
|
|
174
184
|
isValidating: false,
|
|
175
185
|
submissionAttempts: 0,
|
|
@@ -204,6 +214,40 @@ describe('form api', () => {
|
|
|
204
214
|
expect(form.getFieldValue('name')).toEqual('other')
|
|
205
215
|
})
|
|
206
216
|
|
|
217
|
+
it("should be dirty after a field's value has been set", () => {
|
|
218
|
+
const form = new FormApi({
|
|
219
|
+
defaultValues: {
|
|
220
|
+
name: 'test',
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
form.mount()
|
|
224
|
+
|
|
225
|
+
form.setFieldValue('name', 'other', { touch: true })
|
|
226
|
+
|
|
227
|
+
expect(form.state.isDirty).toBe(true)
|
|
228
|
+
expect(form.state.isPristine).toBe(false)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('should be clean again after being reset from a dirty state', () => {
|
|
232
|
+
const form = new FormApi({
|
|
233
|
+
defaultValues: {
|
|
234
|
+
name: 'test',
|
|
235
|
+
},
|
|
236
|
+
})
|
|
237
|
+
form.mount()
|
|
238
|
+
|
|
239
|
+
form.setFieldMeta('name', (meta) => ({
|
|
240
|
+
...meta,
|
|
241
|
+
isDirty: true,
|
|
242
|
+
isPristine: false,
|
|
243
|
+
}))
|
|
244
|
+
|
|
245
|
+
form.reset()
|
|
246
|
+
|
|
247
|
+
expect(form.state.isDirty).toBe(false)
|
|
248
|
+
expect(form.state.isPristine).toBe(true)
|
|
249
|
+
})
|
|
250
|
+
|
|
207
251
|
it("should push an array field's value", () => {
|
|
208
252
|
const form = new FormApi({
|
|
209
253
|
defaultValues: {
|
|
@@ -272,7 +316,7 @@ describe('form api', () => {
|
|
|
272
316
|
|
|
273
317
|
const fieldInArray = new FieldApi({
|
|
274
318
|
form,
|
|
275
|
-
name: `employees
|
|
319
|
+
name: `employees[0].firstName`,
|
|
276
320
|
defaultValue: 'Darcy',
|
|
277
321
|
})
|
|
278
322
|
fieldInArray.mount()
|
|
@@ -300,11 +344,11 @@ describe('form api', () => {
|
|
|
300
344
|
|
|
301
345
|
const fieldInArray = new FieldApi({
|
|
302
346
|
form,
|
|
303
|
-
name: `employees
|
|
347
|
+
name: `employees[0].firstName`,
|
|
304
348
|
defaultValue: 'Darcy',
|
|
305
349
|
})
|
|
306
350
|
fieldInArray.mount()
|
|
307
|
-
form.deleteField(`employees
|
|
351
|
+
form.deleteField(`employees[0].firstName`)
|
|
308
352
|
expect(field.state.value.length).toBe(1)
|
|
309
353
|
expect(Object.keys(field.state.value[0]!).length).toBe(0)
|
|
310
354
|
})
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { assertType } from 'vitest'
|
|
2
|
+
import type { DeepKeys, DeepValue } from '../util-types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Properly recognizes that `0` is not an object and should not have subkeys
|
|
6
|
+
*/
|
|
7
|
+
type TupleSupport = DeepKeys<{ topUsers: [User, 0, User] }>
|
|
8
|
+
assertType<
|
|
9
|
+
| 'topUsers'
|
|
10
|
+
| 'topUsers[0]'
|
|
11
|
+
| 'topUsers[0].name'
|
|
12
|
+
| 'topUsers[0].id'
|
|
13
|
+
| 'topUsers[0].age'
|
|
14
|
+
| 'topUsers[1]'
|
|
15
|
+
| 'topUsers[2]'
|
|
16
|
+
| 'topUsers[2].name'
|
|
17
|
+
| 'topUsers[2].id'
|
|
18
|
+
| 'topUsers[2].age'
|
|
19
|
+
>(0 as never as TupleSupport)
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Properly recognizes that a normal number index won't cut it and should be `[number]` prefixed instead
|
|
23
|
+
*/
|
|
24
|
+
type ArraySupport = DeepKeys<{ users: User[] }>
|
|
25
|
+
assertType<
|
|
26
|
+
| 'users'
|
|
27
|
+
| `users[${number}].name`
|
|
28
|
+
| `users[${number}].id`
|
|
29
|
+
| `users[${number}].age`
|
|
30
|
+
>(0 as never as ArraySupport)
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Properly handles deep object nesting like so:
|
|
34
|
+
*/
|
|
35
|
+
type NestedSupport = DeepKeys<{ meta: { mainUser: User } }>
|
|
36
|
+
assertType<
|
|
37
|
+
| 'meta'
|
|
38
|
+
| 'meta.mainUser'
|
|
39
|
+
| 'meta.mainUser.name'
|
|
40
|
+
| 'meta.mainUser.id'
|
|
41
|
+
| 'meta.mainUser.age'
|
|
42
|
+
>(0 as never as NestedSupport)
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Properly handles `object` edgecase nesting like so:
|
|
46
|
+
*/
|
|
47
|
+
type ObjectNestedEdgecase = DeepKeys<{ meta: { mainUser: object } }>
|
|
48
|
+
assertType<'meta' | 'meta.mainUser' | `meta.mainUser.${string}`>(
|
|
49
|
+
0 as never as ObjectNestedEdgecase,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Properly handles `object` edgecase like so:
|
|
54
|
+
*/
|
|
55
|
+
type ObjectEdgecase = DeepKeys<object>
|
|
56
|
+
assertType<string>(0 as never as ObjectEdgecase)
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Properly handles `object` edgecase nesting like so:
|
|
60
|
+
*/
|
|
61
|
+
type UnknownNestedEdgecase = DeepKeys<{ meta: { mainUser: unknown } }>
|
|
62
|
+
assertType<'meta' | 'meta.mainUser' | `meta.mainUser.${string}`>(
|
|
63
|
+
0 as never as UnknownNestedEdgecase,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Properly handles `object` edgecase like so:
|
|
68
|
+
*/
|
|
69
|
+
type UnknownEdgecase = DeepKeys<unknown>
|
|
70
|
+
assertType<string>(0 as never as UnknownEdgecase)
|
|
71
|
+
|
|
72
|
+
type NestedKeysExample = DeepValue<
|
|
73
|
+
{ meta: { mainUser: User } },
|
|
74
|
+
'meta.mainUser.age'
|
|
75
|
+
>
|
|
76
|
+
assertType<number>(0 as never as NestedKeysExample)
|
|
77
|
+
|
|
78
|
+
type NestedArrayExample = DeepValue<{ users: User[] }, 'users[0].age'>
|
|
79
|
+
assertType<number>(0 as never as NestedArrayExample)
|
|
80
|
+
|
|
81
|
+
type NestedLooseArrayExample = DeepValue<{ users: User[] }, 'users[number].age'>
|
|
82
|
+
assertType<number>(0 as never as NestedLooseArrayExample)
|
|
83
|
+
|
|
84
|
+
type NestedTupleExample = DeepValue<
|
|
85
|
+
{ topUsers: [User, 0, User] },
|
|
86
|
+
'topUsers[0].age'
|
|
87
|
+
>
|
|
88
|
+
assertType<number>(0 as never as NestedTupleExample)
|
|
89
|
+
|
|
90
|
+
type NestedTupleItemExample = DeepValue<
|
|
91
|
+
{ topUsers: [User, 0, User] },
|
|
92
|
+
'topUsers[1]'
|
|
93
|
+
>
|
|
94
|
+
assertType<0>(0 as never as NestedTupleItemExample)
|
|
95
|
+
|
|
96
|
+
type ArrayExample = DeepValue<[1, 2, 3], '[1]'>
|
|
97
|
+
assertType<2>(0 as never as ArrayExample)
|
|
98
|
+
|
|
99
|
+
type NonNestedObjExample = DeepValue<{ a: 1 }, 'a'>
|
|
100
|
+
assertType<1>(0 as never as NonNestedObjExample)
|
|
101
|
+
|
|
102
|
+
interface User {
|
|
103
|
+
name: string
|
|
104
|
+
id: string
|
|
105
|
+
age: number
|
|
106
|
+
}
|
package/src/tests/utils.spec.ts
CHANGED
|
@@ -19,8 +19,8 @@ describe('getBy', () => {
|
|
|
19
19
|
})
|
|
20
20
|
|
|
21
21
|
it('should get array subfields by path', () => {
|
|
22
|
-
expect(getBy(structure, 'kids
|
|
23
|
-
expect(getBy(structure, 'kids
|
|
22
|
+
expect(getBy(structure, 'kids[0].name')).toBe(structure.kids[0]!.name)
|
|
23
|
+
expect(getBy(structure, 'kids[0].age')).toBe(structure.kids[0]!.age)
|
|
24
24
|
})
|
|
25
25
|
})
|
|
26
26
|
|
|
@@ -42,10 +42,10 @@ describe('setBy', () => {
|
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
it('should set array subfields by path', () => {
|
|
45
|
-
expect(setBy(structure, 'kids
|
|
45
|
+
expect(setBy(structure, 'kids[0].name', 'Taylor').kids[0].name).toBe(
|
|
46
46
|
'Taylor',
|
|
47
47
|
)
|
|
48
|
-
expect(setBy(structure, 'kids
|
|
48
|
+
expect(setBy(structure, 'kids[0].age', 20).kids[0].age).toBe(20)
|
|
49
49
|
})
|
|
50
50
|
})
|
|
51
51
|
|
|
@@ -67,14 +67,14 @@ describe('deleteBy', () => {
|
|
|
67
67
|
})
|
|
68
68
|
|
|
69
69
|
it('should delete array subfields by path', () => {
|
|
70
|
-
expect(deleteBy(structure, 'kids
|
|
71
|
-
expect(deleteBy(structure, 'kids
|
|
70
|
+
expect(deleteBy(structure, 'kids[0].name').kids[0].name).not.toBeDefined()
|
|
71
|
+
expect(deleteBy(structure, 'kids[0].age').kids[0].age).not.toBeDefined()
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
it('should delete non-existent paths like a noop', () => {
|
|
75
75
|
expect(deleteBy(structure, 'nonexistent')).toEqual(structure)
|
|
76
76
|
expect(deleteBy(structure, 'nonexistent.nonexistent')).toEqual(structure)
|
|
77
|
-
expect(deleteBy(structure, 'kids
|
|
78
|
-
expect(deleteBy(structure, 'nonexistent
|
|
77
|
+
expect(deleteBy(structure, 'kids[3].name')).toEqual(structure)
|
|
78
|
+
expect(deleteBy(structure, 'nonexistent[3].nonexistent')).toEqual(structure)
|
|
79
79
|
})
|
|
80
80
|
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export type RequiredByKey<T, K extends keyof T> = Omit<T, K> &
|
|
2
|
+
Required<Pick<T, K>>
|
|
3
|
+
|
|
4
|
+
type Narrowable = string | number | bigint | boolean
|
|
5
|
+
|
|
6
|
+
type NarrowRaw<A> =
|
|
7
|
+
| (A extends [] ? [] : never)
|
|
8
|
+
| (A extends Narrowable ? A : never)
|
|
9
|
+
| {
|
|
10
|
+
[K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type Narrow<A> = Try<A, [], NarrowRaw<A>>
|
|
14
|
+
|
|
15
|
+
type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch
|
|
16
|
+
|
|
17
|
+
// Hack to get TypeScript to show simplified types in error messages
|
|
18
|
+
export type Pretty<T> = { [K in keyof T]: T[K] } & {}
|
|
19
|
+
|
|
20
|
+
type ComputeRange<
|
|
21
|
+
N extends number,
|
|
22
|
+
Result extends Array<unknown> = [],
|
|
23
|
+
> = Result['length'] extends N
|
|
24
|
+
? Result
|
|
25
|
+
: ComputeRange<N, [...Result, Result['length']]>
|
|
26
|
+
type Index40 = ComputeRange<40>[number]
|
|
27
|
+
|
|
28
|
+
// Is this type a tuple?
|
|
29
|
+
type IsTuple<T> = T extends readonly any[] & { length: infer Length }
|
|
30
|
+
? Length extends Index40
|
|
31
|
+
? T
|
|
32
|
+
: never
|
|
33
|
+
: never
|
|
34
|
+
|
|
35
|
+
// If this type is a tuple, what indices are allowed?
|
|
36
|
+
type AllowedIndexes<
|
|
37
|
+
Tuple extends ReadonlyArray<any>,
|
|
38
|
+
Keys extends number = never,
|
|
39
|
+
> = Tuple extends readonly []
|
|
40
|
+
? Keys
|
|
41
|
+
: Tuple extends readonly [infer _, ...infer Tail]
|
|
42
|
+
? AllowedIndexes<Tail, Keys | Tail['length']>
|
|
43
|
+
: Keys
|
|
44
|
+
|
|
45
|
+
type PrefixArrayAccessor<T extends any[], TDepth extends any[]> = {
|
|
46
|
+
[K in keyof T]: `[${number}]${DeepKeys<T[K], TDepth>}`
|
|
47
|
+
}[number]
|
|
48
|
+
|
|
49
|
+
type PrefixTupleAccessor<
|
|
50
|
+
T extends any[],
|
|
51
|
+
TIndex extends number,
|
|
52
|
+
TDepth extends any[],
|
|
53
|
+
> = {
|
|
54
|
+
[K in TIndex]: `[${K}]` | `[${K}]${DeepKeys<T[K], TDepth>}`
|
|
55
|
+
}[TIndex]
|
|
56
|
+
|
|
57
|
+
type PrefixObjectAccessor<T extends object, TDepth extends any[]> = {
|
|
58
|
+
[K in keyof T]: K extends string | number
|
|
59
|
+
?
|
|
60
|
+
| PrefixFromDepth<K, TDepth>
|
|
61
|
+
| `${PrefixFromDepth<K, TDepth>}${DeepKeys<T[K], [TDepth]>}`
|
|
62
|
+
: never
|
|
63
|
+
}[keyof T]
|
|
64
|
+
|
|
65
|
+
export type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5
|
|
66
|
+
? never
|
|
67
|
+
: unknown extends T
|
|
68
|
+
? PrefixFromDepth<string, TDepth>
|
|
69
|
+
: object extends T
|
|
70
|
+
? PrefixFromDepth<string, TDepth>
|
|
71
|
+
: T extends readonly any[] & IsTuple<T>
|
|
72
|
+
? PrefixTupleAccessor<T, AllowedIndexes<T>, TDepth>
|
|
73
|
+
: T extends any[]
|
|
74
|
+
? PrefixArrayAccessor<T, [...TDepth, any]>
|
|
75
|
+
: T extends Date
|
|
76
|
+
? never
|
|
77
|
+
: T extends object
|
|
78
|
+
? PrefixObjectAccessor<T, TDepth>
|
|
79
|
+
: never
|
|
80
|
+
|
|
81
|
+
type PrefixFromDepth<
|
|
82
|
+
T extends string | number,
|
|
83
|
+
TDepth extends any[],
|
|
84
|
+
> = TDepth['length'] extends 0 ? T : `.${T}`
|
|
85
|
+
|
|
86
|
+
export type DeepValue<TValue, TAccessor> = TValue extends Record<
|
|
87
|
+
string | number,
|
|
88
|
+
any
|
|
89
|
+
>
|
|
90
|
+
? TAccessor extends `${infer TBefore}[${infer TBrackets}].${infer TAfter}`
|
|
91
|
+
? DeepValue<TValue[TBefore][TBrackets], TAfter>
|
|
92
|
+
: TAccessor extends `[${infer TBrackets}]`
|
|
93
|
+
? DeepValue<TValue, TBrackets>
|
|
94
|
+
: TAccessor extends `${infer TBefore}[${infer TBrackets}]`
|
|
95
|
+
? DeepValue<TValue[TBefore], TBrackets>
|
|
96
|
+
: TAccessor extends `${infer TBefore}.${infer TAfter}`
|
|
97
|
+
? DeepValue<TValue[TBefore], TAfter>
|
|
98
|
+
: TValue[TAccessor & string]
|
|
99
|
+
: never
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ValidationCause
|
|
1
|
+
import type { ValidationCause } from './types'
|
|
2
2
|
import type { FormValidators } from './FormApi'
|
|
3
3
|
import type { FieldValidators } from './FieldApi'
|
|
4
4
|
|
|
@@ -267,75 +267,3 @@ export function getSyncValidatorArray<T>(
|
|
|
267
267
|
return [changeValidator, serverValidator] as never
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
|
-
|
|
271
|
-
export type RequiredByKey<T, K extends keyof T> = Omit<T, K> &
|
|
272
|
-
Required<Pick<T, K>>
|
|
273
|
-
|
|
274
|
-
type ComputeRange<
|
|
275
|
-
N extends number,
|
|
276
|
-
Result extends Array<unknown> = [],
|
|
277
|
-
> = Result['length'] extends N
|
|
278
|
-
? Result
|
|
279
|
-
: ComputeRange<N, [...Result, Result['length']]>
|
|
280
|
-
type Index40 = ComputeRange<40>[number]
|
|
281
|
-
|
|
282
|
-
// Is this type a tuple?
|
|
283
|
-
type IsTuple<T> = T extends readonly any[] & { length: infer Length }
|
|
284
|
-
? Length extends Index40
|
|
285
|
-
? T
|
|
286
|
-
: never
|
|
287
|
-
: never
|
|
288
|
-
|
|
289
|
-
// If this type is a tuple, what indices are allowed?
|
|
290
|
-
type AllowedIndexes<
|
|
291
|
-
Tuple extends ReadonlyArray<any>,
|
|
292
|
-
Keys extends number = never,
|
|
293
|
-
> = Tuple extends readonly []
|
|
294
|
-
? Keys
|
|
295
|
-
: Tuple extends readonly [infer _, ...infer Tail]
|
|
296
|
-
? AllowedIndexes<Tail, Keys | Tail['length']>
|
|
297
|
-
: Keys
|
|
298
|
-
|
|
299
|
-
export type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5
|
|
300
|
-
? never
|
|
301
|
-
: unknown extends T
|
|
302
|
-
? string
|
|
303
|
-
: T extends readonly any[] & IsTuple<T>
|
|
304
|
-
? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>, TDepth>
|
|
305
|
-
: T extends any[]
|
|
306
|
-
? DeepKeysPrefix<T, number, TDepth>
|
|
307
|
-
: T extends Date
|
|
308
|
-
? never
|
|
309
|
-
: T extends object
|
|
310
|
-
? (keyof T & string) | DeepKeysPrefix<T, keyof T, TDepth>
|
|
311
|
-
: never
|
|
312
|
-
|
|
313
|
-
type DeepKeysPrefix<
|
|
314
|
-
T,
|
|
315
|
-
TPrefix,
|
|
316
|
-
TDepth extends any[],
|
|
317
|
-
> = TPrefix extends keyof T & (number | string)
|
|
318
|
-
? `${TPrefix}.${DeepKeys<T[TPrefix], [...TDepth, any]> & string}`
|
|
319
|
-
: never
|
|
320
|
-
|
|
321
|
-
export type DeepValue<T, TProp> = T extends Record<string | number, any>
|
|
322
|
-
? TProp extends `${infer TBranch}.${infer TDeepProp}`
|
|
323
|
-
? DeepValue<T[TBranch], TDeepProp>
|
|
324
|
-
: T[TProp & string]
|
|
325
|
-
: never
|
|
326
|
-
|
|
327
|
-
type Narrowable = string | number | bigint | boolean
|
|
328
|
-
|
|
329
|
-
type NarrowRaw<A> =
|
|
330
|
-
| (A extends [] ? [] : never)
|
|
331
|
-
| (A extends Narrowable ? A : never)
|
|
332
|
-
| {
|
|
333
|
-
[K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
export type Narrow<A> = Try<A, [], NarrowRaw<A>>
|
|
337
|
-
|
|
338
|
-
type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch
|
|
339
|
-
|
|
340
|
-
// Hack to get TypeScript to show simplified types in error messages
|
|
341
|
-
export type Pretty<T> = { [K in keyof T]: T[K] } & {}
|