commit e731f8af332d4f39550f284c8f9e0ff60cbca048
parent 9566528f8da7430cce110ec264caac2bd841aed3
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 14 Sep 2026 17:01:54 -0400
views: the assignability assertion is not distributive
`AssignableTo<A, B> = A extends B ? true : never` DISTRIBUTES over a naked
union type parameter, and both of its arguments here are unions. So it checked
each member of `StreamActionResult` separately and unioned the answers: a
member that does not fit contributes `never`, and `true | never` is `true`.
The assertion passed no matter what, which is worse than not having it.
Measured, not reasoned: renaming `jobId` to `jobIdentifier` in
`StreamActionView` and rebuilding produced ONE error, and it was
`StreamActionLog.tsx:146` reading a property that no longer exists — the
assertion itself stayed silent. With both sides wrapped in tuples the same
sabotage fails at `streamAction.test.ts:26` with "Type 'true' is not assignable
to type 'never'", naming the two types, which is the whole point of putting the
check there rather than leaving twenty call sites to report a structural
mismatch each.
`[A] extends [B]` defeats the distribution and compares the unions whole.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/common/views/streamAction.test.ts b/common/views/streamAction.test.ts
@@ -14,7 +14,15 @@ import type { StreamActionView } from "./streamAction";
//
// tsc sees this file; the runtime assertion below only exists so the file is a
// test rather than a lint the suite would skip.
-type AssignableTo<A, B> = A extends B ? true : never;
+//
+// BOTH SIDES ARE WRAPPED IN TUPLES, and that is the whole assertion. A bare
+// `A extends B ? …` DISTRIBUTES over a naked union type parameter: it would
+// check each member of `StreamActionResult` separately and union the results,
+// so a member that does not fit contributes `never` — and `true | never` is
+// `true`. The check would pass while being wrong about exactly the case it
+// exists to catch. `[A] extends [B]` defeats the distribution and compares the
+// unions whole.
+type AssignableTo<A, B> = [A] extends [B] ? true : never;
const _resultIsAView: AssignableTo<StreamActionResult, StreamActionView> = true;
test("StreamActionResult is assignable to StreamActionView", () => {