DeutschGitHub
prototypepackages/ts-error/src

TS Error

@ralphschuler/ts-error

Captures failures and safely normalizes hostile, cyclic or asynchronous thrown values.

errorsresultnormalization

01 · Problem

What is it for?

JavaScript allows throwing any value, including values whose string conversion itself throws.

02 · Mental model

How should you think about it?

An error boundary should always produce a useful representation and preserve causes without recursive failure.

In this repository

Captures failures and safely normalizes hostile, cyclic or asynchronous thrown values. The compatibility result format still makes an Error returned as ordinary data ambiguous. A tagged Result is a possible future major API.

03 · Control flow

What happens in which order?

  1. Execute a callback inside an error boundary.
  2. Await asynchronous outcomes when applicable.
  3. Preserve successful values.
  4. Normalize a thrown value safely.
  5. Aggregate errors without losing context.

04 · Building blocks

The important contracts

NormalizedError / toNormalizedErrorRepresent unknown thrown values safely.
noThrow / isError / aggregateErrorsCapture async or sync failures, recognize errors and combine messages.

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. Execute a callback inside an error boundary.
  2. Await asynchronous outcomes when applicable.
  3. Preserve successful values.
  4. Normalize a thrown value safely.
  5. Aggregate errors without losing context.
Concept sketch · may reference omitted helpers
type Result<T> =
  | { ok: true; value: T }
  | { ok: false; error: { name: string; message: string; stack?: string } };

async function capture<T>(run: () => T | PromiseLike<T>): Promise<Result<T>> {
  try { return { ok: true, value: await run() }; }
  catch (value) {
    const error = value instanceof Error ? value : new Error(String(value));
    return { ok: false, error: { name: error.name, message: error.message, stack: error.stack } };
  }
}

06 · Verify

What should you test?

  • Test rejected thenables and native promises.
  • Normalize cyclic objects and hostile toString values.
  • Preserve causes and valid falsy return values.

Try it yourself

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

Test rejected thenables and native promises.

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

  • Returning an Error as ordinary data is ambiguous with the current result API.
  • Stack traces are not universally available.
  • Normalization must itself remain safe.
Important

The compatibility result format still makes an Error returned as ordinary data ambiguous. A tagged Result is a possible future major API.

08 · Keep exploring

Source code and related ideas

yarn tsx packages/ts-error/examples/basic.ts