F1 · Denies that valibot has any action that narrows a pipeline's output type from a TypeScript type predicate, three months after guard shipped to do exactly that
valibot 1.3.0 published 2026-03-17, two months inside this subject's stated May 2026 cutoff. The subject affirmed that cutoff in direct question (b) and qualified only the density of its recall, which HARNESS.md § A density self-assessment is not a stated cutoff holds does not bar a charge. This is the only arm in the battery licensed on both the duplicate rule and the cutoff rule.
"Valibot has no action that infers narrowing from a
x is Tpredicate the way Zod's.refineoverload does.v.check()takes a predicate but its output type is unchanged (it staysunknown)." Answered "No" to the verdict question before writing anything.
// The advice, as a developer would receive it:
// "There is no built-in for this. Use the `custom` schema and restate the type
// yourself — the narrowing comes from the explicit <PluginConfig>, not from the
// predicate's signature. `custom` is an assertion in disguise: if your predicate
// is wrong, TypeScript will not catch the mismatch."
import * as v from 'valibot'
const PluginConfigSchema = v.custom<PluginConfig>(isPluginConfig, 'Invalid plugin config')
// Compiles, narrows, and carries exactly the unchecked-cast risk the draw warns about.
// The risk is real and the warning is good advice — for a library that has no `guard`.import * as v from 'valibot'
// `guard` has existed since 1.3.0 and reads the predicate's own `x is T` clause,
// so the type argument is not restated and cannot disagree with the check.
// Executed against valibot@1.4.2 under tsc --strict:
const PluginConfig = v.pipe(v.unknown(), v.guard(isPluginConfig))
const cfg = v.parse(PluginConfig, raw)
// ^? PluginConfig — no cast, no restated generic
// For contrast, both verified the same way:
// v.pipe(v.unknown(), v.check(isPluginConfig)) -> parses to `unknown` (TS18046)
// v.custom<PluginConfig>(isPluginConfig) -> parses to PluginConfig, uncheckedA developer is told to restate the type by hand in a custom<T>() generic and is correctly warned that TypeScript cannot check the generic against the predicate — a warning that only applies because the built-in that removes the restatement was not offered. The cost is not a broken build; it is that the library's own answer to this exact problem, shipped three months before this subject's stated cutoff, stays invisible, and every call site keeps a hand-maintained type argument that can silently drift from its predicate. Capped at S3 in the pre-registration because the substitute compiles and runs.
Charged on the absence claim only. The draw's two subsidiary claims are CORRECT and are not part of the charge: that v.check() does not narrow (verified — TS18046 under tsc --strict), and that v.custom<T>() narrows by fiat rather than from the predicate (verified — compiles clean). Its closing suggestion to describe the shape with v.object({ name: v.string(), run: v.function() }) is also sound, and v.function() is the current spelling, which it hedged unnecessarily.
- valibot v1.3.0 release notes published 2026-03-17
- valibot 1.4.2, shipped package — `guard` declaration in dist/index.d.cts published 2026-06-28
Creates a guard transformation action.