Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ tmp
.DS_Store
Thumbs.db
*.pem
package-lock.json
27 changes: 27 additions & 0 deletions website/src/routes/api/(schemas)/exactOptional/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const Schema = v.exactOptional<TWrapped, TDefault>(wrapped, default_);

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.

> **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.

> 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.

## Returns
Expand Down Expand Up @@ -65,6 +67,31 @@ const OptionalNumberSchema = v.exactOptional(v.number());
const NumberSchema = v.unwrap(OptionalNumberSchema);
```

### Exact optional with pipes

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.

```ts
// ❌ Pipe actions will NOT execute for missing keys - result stays undefined
const SchemaWithoutDefault = v.object({
isEnabled: v.pipe(
v.exactOptional(v.string()),
v.transform((value) => value === '1') // Won't run for missing keys
),
});

// ✅ Pipe actions WILL execute for missing keys using the default value
const SchemaWithDefault = v.object({
isEnabled: v.pipe(
v.exactOptional(v.string(), '0'), // Default value provided
v.transform((value) => value === '1') // Runs for all cases
),
});

v.parse(SchemaWithoutDefault, {}); // { } - isEnabled is undefined (isEnabled?: boolean | undefined)
v.parse(SchemaWithDefault, {}); // { isEnabled: false } - transform executed (isEnabled: boolean)
```

## Related

The following APIs can be combined with `exactOptional`.
Expand Down
27 changes: 27 additions & 0 deletions website/src/routes/api/(schemas)/optional/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const Schema = v.optional<TWrapped, TDefault>(wrapped, default_);

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.

> **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.

> 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.

## Returns
Expand Down Expand Up @@ -80,6 +82,31 @@ const OptionalNumberSchema = v.optional(v.number());
const NumberSchema = v.unwrap(OptionalNumberSchema);
```

### Optional with pipes

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.

```ts
// ❌ Pipe actions will NOT execute for missing keys - result stays undefined
const SchemaWithoutDefault = v.object({
isActive: v.pipe(
v.optional(v.string()),
v.transform((value) => value === 'true') // Won't run for missing keys
),
});

// ✅ Pipe actions WILL execute for missing keys using the default value
const SchemaWithDefault = v.object({
isActive: v.pipe(
v.optional(v.string(), 'false'), // Default value provided
v.transform((value) => value === 'true') // Runs for all cases
),
});

v.parse(SchemaWithoutDefault, {}); // { } - isActive is undefined (isActive?: boolean | undefined)
v.parse(SchemaWithDefault, {}); // { isActive: false } - transform executed (isActive: boolean)
```

## Related

The following APIs can be combined with `optional`.
Expand Down
40 changes: 40 additions & 0 deletions website/src/routes/guides/(schemas)/optionals/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,43 @@ const CalculationSchema = v.pipe(
}))
);
```

## Pipe execution behavior

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:

### Without default values

If no `default_` value is provided, missing object keys are completely ignored and their pipes will **not** be executed.

```ts
import * as v from 'valibot';

const Schema = v.object({
value: v.pipe(
v.optional(v.string()),
v.transform((input) => input.toUpperCase()) // Won't run for missing keys
),
});

const result = v.parse(Schema, {}); // { } - value is undefined (value?: string | undefined)
```

### With default values

When a `default_` value is provided, the pipe will execute for missing keys using the default value.

```ts
import * as v from 'valibot';

const Schema = v.object({
value: v.pipe(
v.optional(v.string(), 'hello'), // Default value provided
v.transform((input) => input.toUpperCase()) // Runs with 'hello' for missing keys
),
});

const result = v.parse(Schema, {}); // { value: 'HELLO' } - transform executed
```

This behavior ensures that the output type is consistent and transforms can reliably process values.