From 43b2f83485e2d2ffa272390e27ed26c9c6b53e8c Mon Sep 17 00:00:00 2001 From: Edwin van den Houdt Date: Thu, 25 Jun 2026 16:55:30 +0200 Subject: [PATCH] Add native-TS functional toolkit (assertNever, Result, Brand) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shared foundation for the "make impossible states impossible" work: - assertNever for compile-time exhaustiveness in union switches - Result + ok/err constructors (plain objects, no classes) - Brand for nominal types No runtime dependency — this is the whole "library". Co-Authored-By: Claude Opus 4.8 --- src/app/core/fp.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/app/core/fp.ts diff --git a/src/app/core/fp.ts b/src/app/core/fp.ts new file mode 100644 index 0000000..872f495 --- /dev/null +++ b/src/app/core/fp.ts @@ -0,0 +1,23 @@ +/** + * Tiny native-TS functional toolkit. No dependency — this is the whole "library". + * Reused by every "impossible states" concept in the POC. + */ + +/** Exhaustiveness guard: put in the `default` arm of a union switch. Adding a + new variant without handling it then fails to compile (x is no longer never). */ +export function assertNever(x: never): never { + throw new Error('Unexpected variant: ' + JSON.stringify(x)); +} + +/** A computation that either succeeded with a value or failed with an error. + Plain objects (no classes) to match the signal/httpResource ergonomics. */ +export type Result = + | { readonly ok: true; readonly value: T } + | { readonly ok: false; readonly error: E }; + +export const ok = (value: T): Result => ({ ok: true, value }); +export const err = (error: E): Result => ({ ok: false, error }); + +/** Nominal typing: Brand is assignable from a plain string + only through an explicit cast — so a smart constructor is the only minter. */ +export type Brand = T & { readonly __brand: B };