@billdaddy/assertkit
v0.1.1
Published
Tiny, type-safe assertions — invariant/assert with TypeScript narrowing, assertDefined, assertNever exhaustiveness, ensure, and lazy messages. Zero dependencies.
Maintainers
Readme
assertkit
Tiny, type-safe assertions —
invariant/assertwith TypeScript narrowing,assertDefined,assertNeverexhaustiveness,ensure, and lazy messages. Zero dependencies.
tiny-invariant throws when a condition is false — but TypeScript still thinks
your value could be null on the next line. assertkit uses real assertion
signatures (asserts condition), so a passing assertion actually narrows the
type. Same tiny footprint, plus assertDefined, exhaustiveness checks, and
lazy messages. Zero dependencies.
import { invariant } from "@billdaddy/assertkit";
function greet(name: string | null) {
invariant(name, "name is required");
return name.toUpperCase(); // ✅ `name` is `string` here
}Why assertkit?
- It narrows.
invariant/assert/assertDefinedcarryassertssignatures, so the compiler refines the type after the call — no!or casts. - Exhaustiveness for free.
assertNevermakes aswitchfail to compile when you add a union member and forget to handle it. - Lazy messages. Pass a
() => string; it only runs when the assertion fails, so expensive diagnostics cost nothing on the happy path. ensurefor inline use. Assert-and-return in a single expression.- One typed error. Everything throws
AssertionError. - Zero dependencies, ESM + CJS + types, ~0.3 kB.
Install
npm install @billdaddy/assertkit
# or: pnpm add @billdaddy/assertkit / yarn add @billdaddy/assertkit / bun add @billdaddy/assertkitAPI
invariant(condition, message?) / assert(condition, message?)
Throw AssertionError when condition is falsy; narrow it to truthy otherwise.
invariant(user, "user not loaded");
invariant(items.length > 0, () => `expected items, got ${items.length}`);assertDefined(value, message?)
Throw when value is null/undefined; narrow to NonNullable<T>. (0, "",
false pass — they're defined.)
assertDefined(config.apiKey, "API key missing");
config.apiKey.slice(0, 4); // string, not string | undefinedensure(value, message?)
Like assertDefined, but returns the value for inline use.
const root = ensure(document.querySelector("#app"), "missing #app");assertNever(value, message?)
Exhaustiveness guard for unions.
type Shape = { kind: "circle" } | { kind: "square" };
function area(s: Shape) {
switch (s.kind) {
case "circle": return /* … */ 0;
case "square": return /* … */ 0;
default: return assertNever(s); // compile error if a case is missing
}
}fail(message?)
Always throws — for unreachable branches or "not implemented" guards.
if (mode === "legacy") fail("legacy mode removed");Contributors ✨
This project follows the all-contributors specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the emoji key for how each contribution is recognized, and open a PR or issue to get involved.
Thanks goes to these wonderful people:
License
MIT © Tung Tran
