DeutschGitHub
partialpackages/assert/src

Assert

@ralphschuler/assert

Enforces runtime conditions with matching TypeScript narrowing.

type narrowinginvariantserrors

01 · Problem

What is it for?

External data does not acquire runtime guarantees merely because a TypeScript type describes it.

02 · Mental model

How should you think about it?

An assertion is a gate: success establishes one precise invariant; failure stops that execution path with a clear error.

In this repository

Enforces runtime conditions with matching TypeScript narrowing. Synchronous assertions throw; nested schemas and asynchronous rejection assertions are separate extensions.

03 · Control flow

What happens in which order?

  1. Receive an unknown value at a boundary.
  2. Evaluate the exact documented condition.
  3. Throw AssertionError on failure.
  4. Continue with a narrowed type on success.

04 · Building blocks

The important contracts

assert / isString / isNumberCheck runtime conditions and narrow types.
isDefined / isNotNullExclude exactly undefined or null.
throws / AssertionErrorVerify synchronous throwing with a distinct error type.

05 · Build it yourself

Implement a small core

Start with this small independent implementation, then use the steps below to make its contract explicit.

  1. Receive an unknown value at a boundary.
  2. Evaluate the exact documented condition.
  3. Throw AssertionError on failure.
  4. Continue with a narrowed type on success.
Concept sketch · may reference omitted helpers
class AssertionError extends Error {}

function assert(value: unknown, message = "Assertion failed"): asserts value {
  if (!value) throw new AssertionError(message);
}

function isDefined<T>(value: T): asserts value is Exclude<T, undefined> {
  assert(value !== undefined, "Expected a defined value");
}

function throws(action: () => unknown) {
  let didThrow = false;
  try { action(); } catch { didThrow = true; }
  assert(didThrow, "Expected action to throw");
}

06 · Verify

What should you test?

  • Check successful and failing inputs for every predicate.
  • Verify that throws rejects a callback that returns normally.
  • Keep null and undefined distinct.

Try it yourself

Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.

Check successful and failing inputs for every predicate.

Compare your approach

Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.

07 · Limits

Trade-offs and failure modes

  • Assertions are synchronous and throw exceptions.
  • Use schemas for nested input validation.
  • An assertion signature must never promise more than its runtime check.
Important

Synchronous assertions throw; nested schemas and asynchronous rejection assertions are separate extensions.

08 · Keep exploring

Source code and related ideas

yarn tsx packages/assert/examples/basic.ts