Skip to content

Types and Runtime Behavior ​

Valchecker carries a type-level execution context alongside the runtime pipeline. The types describe what the current schema means to TypeScript; runtime steps still decide whether an actual value succeeds.

QuestionRuntime viewType-level view
What enters the schema boundary?an untrusted value is executedInferInput<Schema> reads the recorded input state
What can a success contain now?result.valueInferOutput<Schema>
What failures can this pipeline emit?structured issuesInferIssue<Schema>
How can execution complete?sync, maybe-async, or async executorInferOperationMode<Schema> drives the execute() return type

Input and output are different questions ​

A normal public Valchecker instance starts with input: unknown and output: unknown. Initial schema steps validate that unknown boundary and refine the output state. Later transformations can replace the output type again.

That means InferInput is not a shortcut for "the object shape after validation." It is the schema's recorded boundary input type. InferOutput is the type of a successful value after the whole current pipeline.

ts
import type { InferInput, InferOutput } from 'valchecker'
import { v } from 'valchecker'

const lengthSchema = v.string()
	.transform(value => value.length)

type Input = InferInput<typeof lengthSchema>
type Output = InferOutput<typeof lengthSchema>

For an ordinary schema created from the default initial instance, Input remains the unknown external boundary while Output follows the transformation to number.

Each chained method returns a new type state ​

The core Next<...> type patches the current execution context. A validation may add issues or narrow output; a transformation replaces output with its callback result; an async-capable step updates operation mode.

Because chaining also creates a new runtime schema instance, the fluent value and its TypeScript type advance together without mutating the earlier schema.

A successful type does not skip runtime work ​

InferOutput<typeof schema> describes what result.value can be after success. It does not assert that an arbitrary input already has that type, and it does not turn runtime validation into a compile-time cast.

Keep unknown external data unknown until the schema executes. Branch on the returned ExecutionResult before using the inferred output.

Type mode can be more precise than runtime specialization ​

Callback steps such as check() and transform() can infer a precise TypeScript operation mode from their callback return type. The runtime cannot determine callback asynchrony from a function object at schema-construction time, so those steps conservatively register on the maybe-async executor path.

This is not a contradiction: the type state describes the public return contract TypeScript can prove, while the runtime mode chooses a safe execution strategy. See Synchronous and Asynchronous Execution for the observable completion behavior.

Types complement Reference; they do not replace it ​

Type inference can tell you the current input, output, issue union, and completion shape. It does not encode every runtime edge case or option meaning. Exact built-in behavior still belongs to the generated API Reference, while cross-cutting runtime semantics belong to these Core Concepts pages.