Skip to content

Validation and Transformation

Validation and transformation are separate pipeline roles. A validation decides whether the current value may continue. A transformation produces a new successful value for everything that follows.

QuestionValidationTransformation
Primary purposeaccept or reject the current valuechange the successful value or representation
Successful runtime valuepreservedreplaced by the transformed output
Type-state effectmay narrow while keeping the same runtime valueupdates the inferred output type
Generic escape hatchcheck()transform()
Built-in namingusually isXxx()usually toXxx()

Validation preserves the successful value

A validation can reject a value or narrow its type, but a successful validation does not silently normalize that value. If normalization is required, put an explicit transformation in the pipeline.

Transformation changes what later steps receive

A transformation's output becomes the current successful value. Every later validation or transformation sees that new value, and InferOutput tracks the resulting type state.

The checked example below demonstrates both behaviors from one canonical source:

ts
import { v } from 'valchecker'

export const validationOnly = v.string()
	.check(value => value.trim().length > 0)

export const validationResult = validationOnly.execute('  Alice  ')

export const normalized = v.string()
	.transform(value => value.trim())
	.check(value => value.length > 0)

export const transformationResult = normalized.execute('  Alice  ')

The runtime test asserts that validationResult still contains ' Alice ', while transformationResult contains 'Alice'.

Order communicates intent

ts
import { v } from 'valchecker'

const normalizedThenChecked = v.string()
	.transform(value => value.trim())
	.check(value => value.length > 0)

Here the check receives the trimmed value. Reversing those two steps would validate the original string first and transform it only after the validation succeeds.

Use named built-in steps when they precisely express the operation. Use check() and transform() when the behavior is application-defined. Exact callback return forms, issue codes, and options belong to their generated Reference entries rather than this concept page.

Async is a separate dimension

Validation versus transformation describes what a step does to pipeline state. Synchronous versus asynchronous describes how execution completes. Both check() and transform() may participate in a maybe-async pipeline when their callback reaches asynchronous work.

See Synchronous and Asynchronous Execution for that contract.