Skip to content

Commit 721e980

Browse files
authored
fix(types): align error object types with JS object structure (#4961)
* fix(types): align error object types with JS object structure * chore(vee-validate): add changeset * fix(vee-validate): move '' from Path to FromErrors and FormErrorBag The `''` type only makes sense for errors (the top level errors). But the Path type is also used in other places so it shouldn't add it.
1 parent 215e27a commit 721e980

File tree

3 files changed

+57
-25
lines changed

3 files changed

+57
-25
lines changed

.changeset/angry-pens-hug.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"vee-validate": patch
3+
---
4+
5+
Align FormErrors type with its actual structure at runtime.

packages/vee-validate/src/types/forms.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ export interface FormState<TValues> {
182182
submitCount: number;
183183
}
184184

185-
export type FormErrors<TValues extends GenericObject> = Partial<Record<Path<TValues>, string | undefined>>;
186-
export type FormErrorBag<TValues extends GenericObject> = Partial<Record<Path<TValues>, string[]>>;
185+
export type FormErrors<TValues extends GenericObject> = Partial<Record<Path<TValues> | '', string | undefined>>;
186+
export type FormErrorBag<TValues extends GenericObject> = Partial<Record<Path<TValues> | '', string[]>>;
187187

188188
export interface ResetFormOpts {
189189
force: boolean;

packages/vee-validate/src/types/paths.ts

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,37 +72,64 @@ type AnyIsEqual<T1, T2> = T1 extends T2 ? (IsEqual<T1, T2> extends true ? true :
7272
type TupleKeys<T extends ReadonlyArray<any>> = Exclude<keyof T, keyof any[]>;
7373

7474
/**
75-
* Helper type for recursively constructing paths through a type.
76-
* This actually constructs the strings and recurses into nested
77-
* object types.
75+
* Helper type to construct tuple key paths and recurse into its elements.
76+
*
77+
* See {@link Path}
78+
*/
79+
type PathInternalTuple<TValue extends ReadonlyArray<any>, TraversedTypes> = {
80+
[Key in TupleKeys<TValue> & string]: `[${Key}]` | `[${Key}]${PathInternal<TValue[Key], TraversedTypes, false>}`;
81+
}[TupleKeys<TValue> & string];
82+
83+
/**
84+
* Helper type to construct array key paths and recurse into its elements.
85+
*
86+
* See {@link Path}
87+
*/
88+
type PathInternalArray<TValue extends ReadonlyArray<any>, TraversedTypes> =
89+
| `[${ArrayKey}]`
90+
| `[${ArrayKey}]${PathInternal<TValue[ArrayKey], TraversedTypes, false>}`;
91+
92+
/**
93+
* Helper type to construct object key paths and recurse into its nested values.
94+
*
95+
* See {@link Path}
96+
*/
97+
type PathInternalObject<TValue, TraversedTypes, First extends boolean> = {
98+
[Key in keyof TValue & string]: First extends true
99+
? `${Key}` | `${Key}${PathInternal<TValue[Key], TraversedTypes, false>}`
100+
: `.${Key}` | `.${Key}${PathInternal<TValue[Key], TraversedTypes, false>}`;
101+
}[keyof TValue & string];
102+
103+
/**
104+
* Helper type to construct nested any object key paths.
78105
*
79106
* See {@link Path}
80107
*/
81-
type PathImpl<K extends string | number, V, TraversedTypes> = V extends Primitive | BrowserNativeObject
82-
? `${K}`
83-
: // Check so that we don't recurse into the same type
84-
// by ensuring that the types are mutually assignable
85-
// mutually required to avoid false positives of subtypes
86-
true extends AnyIsEqual<TraversedTypes, V>
87-
? `${K}`
88-
: `${K}` | `${K}.${PathInternal<V, TraversedTypes | V>}`;
108+
type PathInternalAny = `.${string}` | `[${string}]` | `[${string}].${string}`;
89109

90110
/**
91111
* Helper type for recursively constructing paths through a type.
92-
* This obscures the internal type param TraversedTypes from ed contract.
112+
*
113+
* This obscures internal type params TraversedTypes and First from ed contract.
93114
*
94115
* See {@link Path}
95116
*/
96-
type PathInternal<T, TraversedTypes = T> =
97-
T extends ReadonlyArray<infer V>
98-
? IsTuple<T> extends true
99-
? {
100-
[K in TupleKeys<T>]-?: PathImpl<K & string, T[K], TraversedTypes>;
101-
}[TupleKeys<T>]
102-
: PathImpl<ArrayKey, V, TraversedTypes>
103-
: {
104-
[K in keyof T]-?: PathImpl<K & string, T[K], TraversedTypes>;
105-
}[keyof T];
117+
type PathInternal<TValue, TraversedTypes, First extends boolean> = TValue extends Primitive | BrowserNativeObject
118+
? IsAny<TValue> extends true
119+
? PathInternalAny
120+
: never
121+
: TValue extends ReadonlyArray<any>
122+
? // Check so that we don't recurse into the same type by ensuring that the
123+
// types are mutually assignable mutually required to avoid false
124+
// positives of subtypes
125+
true extends AnyIsEqual<TraversedTypes, TValue>
126+
? never
127+
: IsTuple<TValue> extends true
128+
? PathInternalTuple<TValue, TraversedTypes | TValue>
129+
: PathInternalArray<TValue, TraversedTypes | TValue>
130+
: TValue extends Record<string, any>
131+
? PathInternalObject<TValue, TraversedTypes | TValue, First>
132+
: '';
106133

107134
/**
108135
* Helper type for recursively constructing paths through a type.
@@ -200,4 +227,4 @@ export type PathValue<T, P extends Path<T> | ArrayPath<T>> = T extends any
200227
*/
201228
// We want to explode the union type and process each individually
202229
// so assignable types don't leak onto the stack from the base.
203-
export type Path<T> = T extends any ? PathInternal<T> : never;
230+
export type Path<T> = T extends any ? PathInternal<T, T, true> & string : never;

0 commit comments

Comments
 (0)