-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Types of fields are invalid when type of validationSchema
is a union
#4925
Comments
Update: seems like types of everything that uses field names only uses them from the first schema: With this revelation, workaround now looks like this: |
values
in submission handler is invalid when type of validationSchema
is a unionvalidationSchema
is a union
validationSchema
is a unionvalidationSchema
is an array
validationSchema
is an arrayvalidationSchema
is a union
Yea, I would think we first need to merge the schemas type-wise, and then infer the path types. I think this is better done in a But I'm not planning on working on a similar API at the moment. Do you think there is a possible workaround we can recommend in the docs for that situation? |
The workaround I'm using is demonstrated in this playground, it is not really trivial though. EDIT: const schemas = [...] as const; // as const is important.
type Enumerate<N extends number, Acc extends number[] = []> = Acc["length"] extends N
? Acc[number]
: Enumerate<N, [...Acc, Acc["length"]]>;
type ArrayIdx<T extends readonly unknown[]> = number extends T["length"]
? number
: Enumerate<T["length"]>;
const currentStep = ref<ArrayIdx<typeof schemas>>(0);
// ^? const currentStep: Ref<0 | 1, 0 | 1> But I think this is too much for a simple workaround. |
You are right, this is too complex to be in the docs. We could expose this as a utility type if possible to make it easier for people to use this. But the DX isn't great here 🤔 |
Not enough time to come up with a full-fledged import type { ArrayIndices, UnionToIntersection } from "type-fest";
import type { InferInput, InferOutput, TypedSchema } from "vee-validate";
import type { Ref } from "vue";
import { computed, readonly, ref, toValue } from "vue";
import type { z } from "zod";
type AnyRef<T> = Parameters<typeof toValue<T>>[0];
type ToValue<T> = T extends AnyRef<infer U> ? U : never;
type RawSchema<T = unknown, V = unknown> = AnyRef<TypedSchema<T, V>>;
type FixedValues<T extends RawSchema> = UnionToIntersection<InferInput<ToValue<T>>>;
type FixedOutput<T extends RawSchema> = UnionToIntersection<InferOutput<ToValue<T>>>;
type FixedSchema<T extends RawSchema> = TypedSchema<FixedValues<T>, FixedOutput<T>>;
export function useMultistepSchema<
RawValues extends Record<string, unknown>,
RawOutput extends Record<string, unknown>,
RawSchemas extends readonly [
RawSchema<RawValues, RawOutput>,
...RawSchema<RawValues, RawOutput>[],
],
>(schemas: RawSchemas) {
const currentStep = ref(0);
const currentSchema = computed(() => toValue(schemas[currentStep.value]));
return {
currentSchema: readonly(currentSchema as Ref<FixedSchema<RawSchemas[number]>>),
currentStep: currentStep as Ref<ArrayIndices<RawSchemas>>,
};
} Usage: const { currentStep, currentSchema } = useMultistepSchema([
computed(() =>
toTypedSchema(
z.object({
field1: z.string(),
field2: z.string(),
}),
),
),
toTypedSchema(
z.object({
field3: z.string(),
field4: z.string(),
}),
),
]);
const { handleSubmit, values } = useForm({
validationSchema: currentSchema,
keepValuesOnUnmount: true,
initialValues: { field4: "" },
});
handleSubmit((values) => {
if (currentStep.value === 0) return (currentStep.value = 1);
console.log(values);
}); Additional note about zod: as per #4284 (comment), you need to make all your schemas
To combat this, the schema needs to be made passthrough silently, and typescript need not be informed about it: import { toTypedSchema as _toTypedSchema } from "@vee-validate/zod";
function toTypedSchema<T extends z.ZodObject<z.ZodRawShape, "strip">>(
schema: T,
) {
return _toTypedSchema(schema.passthrough() as unknown as T);
} After that all unknown fields are missing, and types of functions like
|
What happened?
Trying to implement a multi-step form. Type of
values
inside the submit handler only has the fields from the first step:I'd expect all the fields to be present.
It is possible to obtain the expected type with a bunch of type trickery:
Reproduction steps
N/A
Version
Vue.js 3.x and vee-validate 4.x
What browsers are you seeing the problem on?
N/A
Relevant log output
N/A
Demo link
https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgVwM4FMBi0QBo4wCeY6cAkgHYBm6UA8sjGI3AL5xVQQhwBEAbunQBafgEMANsAAmYmOl4BuAFChIsRAQgAVYumkBlAMYALdCDFsOXHrwACgkeKmz5AegBeEaUtXho8EgeVpzcfF4+KmoBmlDoVPhG3Mzy0iE2fPzICirKSRSo8Kim5mKocAC8cADaynBauiSGJRYAFHX1cB4AdBAARgBW6EYwrQgdnRzA6BLSAIwAXF3dhVDAFADmrQCUuBOdVNOzAExLPavrW7v7rNf1dw16zWZt+z39QyNj+-WHM9IAZjOKxga02Oz2k1+R2kABZgRdwQ96rdIXBrgBdXL5QpwIzIKBxCgwAzyMCVOBxKgAHgADHAAD5wOYAPlatO2Khx8HxhPQxOML0sVSS4EY+laO0qLLgxSFqGqvKJJLJ3Wc2QxAEJOco8hACoE4CYxBRpBJ0AZkH0QMB4OwqmgsDhvvVnDI5MB9YLSkslfySS0xHtbrljabzZbrbbJQAPbbSxAdNxuAh6OAQKhwGNLcb1ZNQv6zRay0GXFR5lOTQvSU4lsEbctwfOsXX1blTGP6AAaFJjcDKcEwwE7z1KKhDuqIJDgAFUKJ6KNoIJR5FAMCMF9SZzKqq0Z3B0DH5KbyibCHAAPxwVrZ2fxioy-gQGRwJYUdCCKDxw-H6Tldr1Le6w0FA5B7Pej7PtIHRXmQHRvh+tC5FOpBDiO3oWBSc4LkuK60OuMCbpQIEMEwjDUq0KEZrKgaoNs1QUMgIB9LQGIsiyKhAA
Code of Conduct
The text was updated successfully, but these errors were encountered: