Skip to content

Commit 5d2c8dd

Browse files
chorobinBalastrong
andauthored
fix(form-core): types to pass most tests (#1411)
* fix: types to pass most tests * refactor: improve naming * refactor: fix tests and types * chore: fix tests * chore: cleanup --------- Co-authored-by: Leonardo Montini <[email protected]>
1 parent faf4cbd commit 5d2c8dd

File tree

3 files changed

+160
-82
lines changed

3 files changed

+160
-82
lines changed

packages/form-core/src/util-types.ts

+90-35
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,58 @@ export type Narrow<A> = Try<A, [], NarrowRaw<A>>
2121

2222
type IsAny<T> = 0 extends 1 & T ? true : false
2323

24-
export type ArrayAccessor<TPrefix extends string> = `${TPrefix}[${number}]`
24+
export interface AnyDeepKeyAndValue {
25+
key: string
26+
value: any
27+
}
28+
29+
export type ArrayAccessor<TParent extends AnyDeepKeyAndValue> =
30+
`${TParent['key'] extends never ? '' : TParent['key']}[${number}]`
31+
32+
export interface ArrayDeepKeyAndValue<
33+
in out TParent extends AnyDeepKeyAndValue,
34+
in out T extends ReadonlyArray<any>,
35+
> {
36+
key: ArrayAccessor<TParent>
37+
value: T[number] | Nullable<TParent['value']>
38+
}
2539

26-
export type DeepRecordArrayUnion<
40+
export type DeepKeyAndValueArray<
41+
TParent extends AnyDeepKeyAndValue,
2742
T extends ReadonlyArray<any>,
28-
TPrefix extends string,
2943
TAcc,
30-
> = DeepRecordUnion<
31-
T[number],
32-
ArrayAccessor<TPrefix>,
33-
TAcc | Record<ArrayAccessor<TPrefix>, T[number]>
44+
> = DeepKeysAndValues<
45+
NonNullable<T[number]>,
46+
ArrayDeepKeyAndValue<TParent, T>,
47+
TAcc | ArrayDeepKeyAndValue<TParent, T>
3448
>
3549

3650
export type TupleAccessor<
37-
TPrefix extends string,
38-
TKey,
39-
> = `${TPrefix}[${TKey & string}]`
51+
TParent extends AnyDeepKeyAndValue,
52+
TKey extends string,
53+
> = `${TParent['key'] extends never ? '' : TParent['key']}[${TKey}]`
54+
55+
export interface TupleDeepKeyAndValue<
56+
in out TParent extends AnyDeepKeyAndValue,
57+
in out T,
58+
in out TKey extends AllTupleKeys<T>,
59+
> {
60+
key: TupleAccessor<TParent, TKey>
61+
value: T[TKey] | Nullable<TParent['value']>
62+
}
4063

4164
export type AllTupleKeys<T> = T extends any ? keyof T & `${number}` : never
4265

43-
export type DeepRecordTupleUnion<
66+
export type DeepKeyAndValueTuple<
67+
TParent extends AnyDeepKeyAndValue,
4468
T extends ReadonlyArray<any>,
45-
TPrefix extends string,
4669
TAcc,
4770
TAllKeys extends AllTupleKeys<T> = AllTupleKeys<T>,
4871
> = TAllKeys extends any
49-
? DeepRecordUnion<
50-
T[TAllKeys],
51-
TupleAccessor<TPrefix, TAllKeys>,
52-
TAcc | Record<TupleAccessor<TPrefix, TAllKeys>, T[TAllKeys]>
72+
? DeepKeysAndValues<
73+
NonNullable<T[TAllKeys]>,
74+
TupleDeepKeyAndValue<TParent, T, TAllKeys>,
75+
TAcc | TupleDeepKeyAndValue<TParent, T, TAllKeys>
5376
>
5477
: never
5578

@@ -58,53 +81,85 @@ export type AllObjectKeys<T> = T extends any
5881
: never
5982

6083
export type ObjectAccessor<
61-
TPrefix extends string,
84+
TParent extends AnyDeepKeyAndValue,
6285
TKey extends string | number,
63-
> = TPrefix extends '' ? `${TKey}` : `${TPrefix}.${TKey}`
86+
> = TParent['key'] extends never ? `${TKey}` : `${TParent['key']}.${TKey}`
87+
88+
export type Nullable<T> = T & (undefined | null)
89+
90+
export interface ObjectDeepKeyAndValue<
91+
in out TParent extends AnyDeepKeyAndValue,
92+
in out T,
93+
in out TKey extends AllObjectKeys<T>,
94+
> {
95+
key: ObjectAccessor<TParent, TKey>
96+
value: T[TKey] | Nullable<TParent['value']>
97+
}
6498

65-
export type DeepRecordObjectUnion<
99+
export type DeepKeyAndValueObject<
100+
TParent extends AnyDeepKeyAndValue,
66101
T,
67-
TPrefix extends string,
68102
TAcc,
69103
TAllKeys extends AllObjectKeys<T> = AllObjectKeys<T>,
70104
> = TAllKeys extends any
71-
? DeepRecordUnion<
72-
T[TAllKeys],
73-
ObjectAccessor<TPrefix, TAllKeys>,
74-
TAcc | Record<ObjectAccessor<TPrefix, TAllKeys>, T[TAllKeys]>
105+
? DeepKeysAndValues<
106+
NonNullable<T[TAllKeys]>,
107+
ObjectDeepKeyAndValue<TParent, T, TAllKeys>,
108+
TAcc | ObjectDeepKeyAndValue<TParent, T, TAllKeys>
75109
>
76110
: never
77111

78-
export type DeepRecordUnion<T, TPrefix extends string = '', TAcc = never> =
112+
export type UnknownAccessor<TParent extends AnyDeepKeyAndValue> =
113+
TParent['key'] extends never ? string : `${TParent['key']}.${string}`
114+
115+
export interface UnknownDeepKeyAndValue<TParent extends AnyDeepKeyAndValue> {
116+
key: UnknownAccessor<TParent>
117+
value: unknown
118+
}
119+
120+
export type DeepKeyAndValueUnknown<TParent extends AnyDeepKeyAndValue> =
121+
UnknownDeepKeyAndValue<TParent>
122+
123+
export type DeepKeysAndValues<
124+
T,
125+
TParent extends AnyDeepKeyAndValue = never,
126+
TAcc = never,
127+
> =
79128
IsAny<T> extends true
80129
? T
81130
: T extends string | number | boolean | bigint | Date
82131
? TAcc
83132
: T extends ReadonlyArray<any>
84133
? number extends T['length']
85-
? DeepRecordArrayUnion<T, TPrefix, TAcc>
86-
: DeepRecordTupleUnion<T, TPrefix, TAcc>
87-
: T extends object
88-
? DeepRecordObjectUnion<T, TPrefix, TAcc>
89-
: TAcc
134+
? DeepKeyAndValueArray<TParent, T, TAcc>
135+
: DeepKeyAndValueTuple<TParent, T, TAcc>
136+
: keyof T extends never
137+
? TAcc | DeepKeyAndValueUnknown<TParent>
138+
: T extends object
139+
? DeepKeyAndValueObject<TParent, T, TAcc>
140+
: TAcc
90141

91142
export type DeepRecord<T> = {
92-
[TRecord in DeepRecordUnion<T> as keyof TRecord]: TRecord[keyof TRecord]
143+
[TRecord in DeepKeysAndValues<T> extends AnyDeepKeyAndValue
144+
? DeepKeysAndValues<T>
145+
: never as TRecord['key']]: TRecord['value']
93146
}
94147

95-
type UnionKeys<T> = T extends any ? keyof T : never
96-
97148
/**
98149
* The keys of an object or array, deeply nested.
99150
*/
100151
export type DeepKeys<T> = unknown extends T
101152
? string
102-
: UnionKeys<DeepRecordUnion<T>> & string
153+
: DeepKeysAndValues<T> extends AnyDeepKeyAndValue
154+
? DeepKeysAndValues<T>['key']
155+
: never
103156

104157
/**
105158
* Infer the type of a deeply nested property within an object or an array.
106159
*/
107160
export type DeepValue<TValue, TAccessor> =
108161
DeepRecord<TValue> extends infer TDeepRecord
109-
? TDeepRecord[TAccessor & keyof TDeepRecord]
162+
? TAccessor extends keyof TDeepRecord
163+
? TDeepRecord[TAccessor]
164+
: never
110165
: never

packages/form-core/tests/FieldApi.test-d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ it('should type an array sub-field properly', () => {
134134

135135
const field = new FieldApi({
136136
form,
137-
name: `nested.people[${1}].name`,
137+
name: `nested.people[1].name`,
138138
validators: {
139139
onChangeAsync: async ({ value }) => {
140140
assertType<string>(value)

0 commit comments

Comments
 (0)