Skip to content

Commit f04aab6

Browse files
committed
chore: pnpmize docs folder
1 parent 303b1fb commit f04aab6

File tree

4 files changed

+3262
-5132
lines changed

4 files changed

+3262
-5132
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
---
2+
layout: ../../../layouts/PageLayout.astro
3+
title: Typed Schemas
4+
description: Type-safe input and output types
5+
order: 5
6+
next: guide/composition-api/api-review
7+
menuTitle: Caveats
8+
new: true
9+
---
10+
11+
import DocTip from '@/components/DocTip.vue';
12+
import DocBadge from '@/components/DocBadge.vue';
13+
14+
# Typed Schemas <DocBadge title="v4.8" />
15+
16+
<DocTip type="warn">
17+
18+
This guide is aimed towards TypeScript usage with vee-validate's composition API. If you are using the components API or JavaScript then there isn't much to learn here.
19+
20+
</DocTip>
21+
22+
## Input and Output types
23+
24+
Consider the following form:
25+
26+
```ts
27+
import { useForm } from 'vee-validate';
28+
import { object, string } from 'yup';
29+
30+
const { values, handleSubmit } = useForm({
31+
validationSchema: object({
32+
email: string().required(),
33+
password: string().required(),
34+
name: string(),
35+
}),
36+
});
37+
```
38+
39+
When attempting to interact with the `values` you will notice that it is untyped. Meaning you don't get any type hints or checks which makes it less safe to use.
40+
41+
```ts
42+
// 💥 Blows up because `email` is undefined
43+
values.email.endsWith('@gmail.com');
44+
```
45+
46+
Providing an `initialValues` or an interface to `useForm` could help:
47+
48+
```ts
49+
interface MyForm {
50+
email?: string | null;
51+
password?: string | null;
52+
}
53+
54+
const { values, handleSubmit } = useForm<MyForm>({
55+
validationSchema: object({
56+
email: string().required(),
57+
password: string().required(),
58+
name: string(),
59+
}),
60+
});
61+
62+
// ❌ Type error, which means `values` is type-safe
63+
values.email.endsWith('@gmail.com');
64+
```
65+
66+
But then you will find another problem when using `handleSubmit`:
67+
68+
```ts
69+
handleSubmit(values => {
70+
// Must be checked, this means submmited values are innacurate
71+
if (values.email) {
72+
values.email.endsWith('@gmail.com');
73+
}
74+
});
75+
```
76+
77+
Even though you marked your field as `required`, it still uses the same type you provider earlier which will keep the `email` field as nullable. This is what we mean by the "dual nature" of form values.
78+
79+
Form values can exist in two types/versions:
80+
81+
- The **"input"** type which is the one the user is filling/interacting with, it is the one that captures the user input.
82+
- The **"output"** type which is the one the user ends up submitting.
83+
84+
You can tell vee-validate about this information by using the typed schema resolvers available in compainion packages.
85+
86+
## Yup
87+
88+
You can use yup as a typed schema with the `@vee-validate/yup` package:
89+
90+
```sh
91+
# npm
92+
npm install @vee-validate/yup
93+
# yarn
94+
yarn add @vee-validate/yup
95+
# pnpm
96+
pnpm add @vee-validate/yup
97+
```
98+
99+
The `@vee-valdiate/yup` package exposes a `toTypedSchema` function that accepts any yup schema. Which then you can pass along to `validationSchema` option on `useForm`.
100+
101+
This makes the form values and submitted values typed automatically and caters for both input and output types of that schema.
102+
103+
```ts
104+
import { useForm } from 'vee-validate';
105+
import { object, string } from 'yup';
106+
import { toTypedSchema } from '@vee-validate/yup';
107+
108+
const { values, handleSubmit } = useForm({
109+
validationSchema: toTypedSchema(
110+
object({
111+
email: string().required(),
112+
password: string().required(),
113+
name: string(),
114+
})
115+
),
116+
});
117+
118+
// ❌ Type error, which means `values` is type-safe
119+
values.email.endsWith('@gmail.com');
120+
121+
handleSubmit(submitted => {
122+
// No errors, because email is required!
123+
submitted.email.endsWith('@gmail.com');
124+
125+
// ❌ Type error, because `name` is not required so it could be undefined
126+
// Means that your fields are now type safe!
127+
submitted.name.length;
128+
});
129+
```
130+
131+
### Yup default values
132+
133+
You can also define default values on your schema directly and it will be picked up by the form:
134+
135+
```ts
136+
import { useForm } from 'vee-validate';
137+
import { object, string } from 'yup';
138+
import { toTypedSchema } from '@vee-validate/yup';
139+
140+
const { values, handleSubmit } = useForm({
141+
validationSchema: toTypedSchema(
142+
object({
143+
email: string().required().default('[email protected]'),
144+
password: string().required().default(''),
145+
name: string().default(''),
146+
})
147+
),
148+
});
149+
```
150+
151+
Your initial values will be using the schema defaults, and also the defaults will be used if the values submitted is missing these fields.
152+
153+
### Yup transforms
154+
155+
You can also define transforms to cast your fields before submission:
156+
157+
```ts
158+
import { useForm } from 'vee-validate';
159+
import { object, number } from 'yup';
160+
import { toTypedSchema } from '@vee-validate/yup';
161+
162+
const { values, handleSubmit } = useForm({
163+
validationSchema: toTypedSchema(
164+
object({
165+
age: number()
166+
.transform(val => Number(val))
167+
.required(),
168+
})
169+
),
170+
});
171+
```
172+
173+
But note that this does not change the input or output types of the casted fields. The cast will only occur when setting the initial value and when the values are submitted.
174+
175+
## Zod
176+
177+
Zod is an excellent library for value validation which mirrors static typing APIs.
178+
179+
In their own words it is a:
180+
181+
> TypeScript-first schema validation with static type inference
182+
183+
You can use zod as a typed schema with the `@vee-validate/zod` package:
184+
185+
```sh
186+
# npm
187+
npm install @vee-validate/zod
188+
# yarn
189+
yarn add @vee-validate/zod
190+
# pnpm
191+
pnpm add @vee-validate/zod
192+
```
193+
194+
The `@vee-valdiate/zod` package exposes a `toTypedSchema` function that accepts any zod schema. Which then you can pass along to `validationSchema` option on `useForm`.
195+
196+
This makes the form values and submitted values typed automatically and caters for both input and output types of that schema.
197+
198+
```ts
199+
import { useForm } from 'vee-validate';
200+
import { object, string } from 'yup';
201+
import { toTypedSchema } from '@vee-validate/yup';
202+
203+
const { values, handleSubmit } = useForm({
204+
validationSchema: toTypedSchema(
205+
object({
206+
email: string().min(1, 'required'),
207+
password: string().min(1, 'required'),
208+
name: string().optional(),
209+
})
210+
),
211+
});
212+
213+
// ❌ Type error, which means `values` is type-safe
214+
values.email.endsWith('@gmail.com');
215+
216+
handleSubmit(submitted => {
217+
// No errors, because email is required!
218+
submitted.email.endsWith('@gmail.com');
219+
220+
// ❌ Type error, because `name` is not required so it could be undefined
221+
// Means that your fields are now type safe!
222+
submitted.name.length;
223+
});
224+
```
225+
226+
### Zod default values
227+
228+
You can also define default values on your zod schema directly and it will be picked up by the form:
229+
230+
```ts
231+
import { useForm } from 'vee-validate';
232+
import { object, string } from 'yup';
233+
import { toTypedSchema } from '@vee-validate/yup';
234+
235+
const { values, handleSubmit } = useForm({
236+
validationSchema: toTypedSchema(
237+
object({
238+
email: string().default('[email protected]'),
239+
password: string().default(''),
240+
})
241+
),
242+
});
243+
```
244+
245+
Your initial values will be using the schema defaults, and also the defaults will be used if the values submitted is missing these fields.
246+
247+
### Zod preprocess
248+
249+
You can also define preprocessors to cast your fields before submission:
250+
251+
```ts
252+
import { useForm } from 'vee-validate';
253+
import { object, number, preprocess } from 'yup';
254+
import { toTypedSchema } from '@vee-validate/yup';
255+
256+
const { values, handleSubmit } = useForm({
257+
validationSchema: toTypedSchema(
258+
object({
259+
age: preprocess(val => Number(val), number()),
260+
})
261+
),
262+
});
263+
264+
// typed as `unknown` since the source value can be anything
265+
values.age;
266+
267+
handleSubmit(submitted => {
268+
// will be typed as number because zod made sure it is!
269+
values.age;
270+
});
271+
```

0 commit comments

Comments
 (0)