Skip to content

Commit 650d9da

Browse files
CopilotPerdolique
authored andcommitted
docs: clarify pipe behavior for missing keys in optional schemas
- Add important notes about pipe execution for missing keys - Explain that default values are required for pipe execution - Improve clarity across optional, exactOptional, and guides - Enhance understanding of transformation behavior
1 parent ca72ba9 commit 650d9da

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

website/src/routes/api/(schemas)/exactOptional/index.mdx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const Schema = v.exactOptional<TWrapped, TDefault>(wrapped, default_);
3232

3333
With `exactOptional` the validation of your schema will pass missing object entries, and if you specify a `default_` input value, the schema will use it if the object entry is missing. For this reason, the output type may differ from the input type of the schema.
3434

35+
> **Important**: When a key is missing and no `default_` value is provided, the schema's pipe (including transformations) will not be executed. To ensure pipes run for missing keys, provide a `default_` value.
36+
3537
> The difference to <Link href="../optional/">`optional`</Link> is that this schema function follows the implementation of TypeScript's [`exactOptionalPropertyTypes` configuration](https://www.typescriptlang.org/tsconfig/#exactOptionalPropertyTypes) and only allows missing but not undefined object entries.
3638
3739
## Returns
@@ -65,6 +67,31 @@ const OptionalNumberSchema = v.exactOptional(v.number());
6567
const NumberSchema = v.unwrap(OptionalNumberSchema);
6668
```
6769

70+
### Exact optional with pipes
71+
72+
When using `exactOptional` in a <Link href="../../pipe/">`pipe`</Link>, the pipe actions only execute if a `default_` value is provided or the key is present. This applies to all pipe actions including <Link href="../../transform/">`transform`</Link>, <Link href="../../check/">`check`</Link>, and others.
73+
74+
```ts
75+
// ❌ Pipe actions will NOT execute for missing keys - result stays undefined
76+
const SchemaWithoutDefault = v.object({
77+
isEnabled: v.pipe(
78+
v.exactOptional(v.string()),
79+
v.transform((value) => value === '1') // Won't run for missing keys
80+
),
81+
});
82+
83+
// ✅ Pipe actions WILL execute for missing keys using the default value
84+
const SchemaWithDefault = v.object({
85+
isEnabled: v.pipe(
86+
v.exactOptional(v.string(), '0'), // Default value provided
87+
v.transform((value) => value === '1') // Runs for all cases
88+
),
89+
});
90+
91+
v.parse(SchemaWithoutDefault, {}); // { } - isEnabled is undefined (isEnabled?: boolean | undefined)
92+
v.parse(SchemaWithDefault, {}); // { isEnabled: false } - transform executed (isEnabled: boolean)
93+
```
94+
6895
## Related
6996

7097
The following APIs can be combined with `exactOptional`.

website/src/routes/api/(schemas)/optional/index.mdx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ const Schema = v.optional<TWrapped, TDefault>(wrapped, default_);
3333

3434
With `optional` the validation of your schema will pass `undefined` inputs, and if you specify a `default_` input value, the schema will use it if the input is `undefined`. For this reason, the output type may differ from the input type of the schema.
3535

36+
> **Important**: When used in object schemas, if a key is missing and no `default_` value is provided, the schema's pipe (including transformations) will not be executed. To ensure pipes run for missing keys, provide a `default_` value.
37+
3638
> Note that `optional` does not accept `null` as an input. If you want to accept `null` inputs, use <Link href="../nullable/">`nullable`</Link>, and if you want to accept `null` and `undefined` inputs, use <Link href="../nullish/">`nullish`</Link> instead. Also, if you want to set a default output value for any invalid input, you should use <Link href="../fallback/">`fallback`</Link> instead.
3739
3840
## Returns
@@ -80,6 +82,31 @@ const OptionalNumberSchema = v.optional(v.number());
8082
const NumberSchema = v.unwrap(OptionalNumberSchema);
8183
```
8284

85+
### Optional with pipes
86+
87+
When using `optional` in a <Link href="../../pipe/">`pipe`</Link>, the pipe actions only execute if a `default_` value is provided or the key is present. This applies to all pipe actions including <Link href="../../transform/">`transform`</Link>, <Link href="../../check/">`check`</Link>, and others.
88+
89+
```ts
90+
// ❌ Pipe actions will NOT execute for missing keys - result stays undefined
91+
const SchemaWithoutDefault = v.object({
92+
isActive: v.pipe(
93+
v.optional(v.string()),
94+
v.transform((value) => value === 'true') // Won't run for missing keys
95+
),
96+
});
97+
98+
// ✅ Pipe actions WILL execute for missing keys using the default value
99+
const SchemaWithDefault = v.object({
100+
isActive: v.pipe(
101+
v.optional(v.string(), 'false'), // Default value provided
102+
v.transform((value) => value === 'true') // Runs for all cases
103+
),
104+
});
105+
106+
v.parse(SchemaWithoutDefault, {}); // { } - isActive is undefined (isActive?: boolean | undefined)
107+
v.parse(SchemaWithDefault, {}); // { isActive: false } - transform executed (isActive: boolean)
108+
```
109+
83110
## Related
84111

85112
The following APIs can be combined with `optional`.

website/src/routes/guides/(schemas)/optionals/index.mdx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,43 @@ const CalculationSchema = v.pipe(
8888
}))
8989
);
9090
```
91+
92+
## Pipe execution behavior
93+
94+
When using <Link href="/api/optional/">`optional`</Link>, <Link href="/api/exactOptional/">`exactOptional`</Link>, or other optional schemas with <Link href="/api/pipe/">`pipe`</Link> containing actions, it's important to understand when the pipe executes:
95+
96+
### Without default values
97+
98+
If no `default_` value is provided, missing object keys are completely ignored and their pipes will **not** be executed.
99+
100+
```ts
101+
import * as v from 'valibot';
102+
103+
const Schema = v.object({
104+
value: v.pipe(
105+
v.optional(v.string()),
106+
v.transform((input) => input.toUpperCase()) // Won't run for missing keys
107+
),
108+
});
109+
110+
const result = v.parse(Schema, {}); // { } - value is undefined (value?: string | undefined)
111+
```
112+
113+
### With default values
114+
115+
When a `default_` value is provided, the pipe will execute for missing keys using the default value.
116+
117+
```ts
118+
import * as v from 'valibot';
119+
120+
const Schema = v.object({
121+
value: v.pipe(
122+
v.optional(v.string(), 'hello'), // Default value provided
123+
v.transform((input) => input.toUpperCase()) // Runs with 'hello' for missing keys
124+
),
125+
});
126+
127+
const result = v.parse(Schema, {}); // { value: 'HELLO' } - transform executed
128+
```
129+
130+
This behavior ensures that the output type is consistent and transforms can reliably process values.

0 commit comments

Comments
 (0)