🔎 Search Terms
morph never, tuple morph infer never, => morph parameter never, scope morph output never, unannotated morph parameter
🧩 Context
- ArkType version: 2.2.0
- TypeScript version: 5.9.3
- Other context you think may be relevant (JS flavor, OS, etc.): Node 24.11.1, macOS 26.5 (arm64),
--strict
🧑💻 Repro
Playground Link
import { scope } from "arktype"
const m = scope({
Base: { text: "string" },
// ❌ tuple morph, parameter NOT annotated, return DEPENDS on the parameter
// -> `.infer` (output) is `never`
bug: ["Base", "=>", (d) => d],
// ✅ same morph with the parameter annotated -> `.infer` = { text: string }
fixedByAnnotation: ["Base", "=>", (d: { text: string }) => d],
// ✅ morph that IGNORES the parameter -> infers fine
okIgnoresParam: ["Base", "=>", () => ({ n: 1 })]
}).export()
// Probes — a type error on the line means that case's `.infer` is `never`:
const a: typeof m.bug.infer = { text: "x" } // ❌ Type '{ text: string }' is not assignable to type 'never'
const b: typeof m.fixedByAnnotation.infer = { text: "x" } // ✅ ok
const c: typeof m.okIgnoresParam.infer = { n: 1 } // ✅ ok
Summary
In a tuple-expression morph [In, "=>", fn], when fn's parameter is not explicitly annotated and fn's return type depends on that parameter, the resulting type's output (.infer) collapses to never.
Observations:
- It is not object-specific —
["string", "=>", (s) => s.length] collapses identically.
- It is specifically about a parameter-dependent return. A morph that ignores its parameter and returns a fresh value (
() => ({ n: 1 })) infers correctly.
- TypeScript emits no
TS7006 (implicit-any) on the un-annotated parameter, so arktype is supplying a contextual input type — yet the output is still never. This suggests a circular inference: the parameter's contextual type appears to depend on the morph's output, which in turn depends on the parameter.
- Workarounds: annotate the parameter (
(d: { text: string }) => d), or use the .pipe() method form (type({ text: "string" }).pipe((d) => d)), both of which infer the output correctly.
This is easy to hit in practice: an object-level normalizing morph (return the same object with one field rewritten) silently turns the whole type — and anything that references it — into never.
🔎 Search Terms
morph never, tuple morph infer never,
=>morph parameter never, scope morph output never, unannotated morph parameter🧩 Context
--strict🧑💻 Repro
Playground Link
Summary
In a tuple-expression morph
[In, "=>", fn], whenfn's parameter is not explicitly annotated andfn's return type depends on that parameter, the resulting type's output (.infer) collapses tonever.Observations:
["string", "=>", (s) => s.length]collapses identically.() => ({ n: 1 })) infers correctly.TS7006(implicit-any) on the un-annotated parameter, so arktype is supplying a contextual input type — yet the output is stillnever. This suggests a circular inference: the parameter's contextual type appears to depend on the morph's output, which in turn depends on the parameter.(d: { text: string }) => d), or use the.pipe()method form (type({ text: "string" }).pipe((d) => d)), both of which infer the output correctly.This is easy to hit in practice: an object-level normalizing morph (return the same object with one field rewritten) silently turns the whole type — and anything that references it — into
never.