outvariant

Type-safe invariant assertions with printf-style positional formatting for cleaner error messages.

Library
npm
v1.4.3
54stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
29/100Needs Attention
Development Activity0
Maintenance20
Community28
Maturity56
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
63/100Good
Architecture80
Code Quality75
Innovation65
Learning Curve30

Outvariant is a lightweight TypeScript implementation of the classic invariant pattern, the shorthand assertion function used to guard preconditions instead of writing manual if/throw blocks. Unlike many invariant implementations, it is built with TypeScript’s control-flow analysis in mind: after a call to invariant(), the checked value is typed as non-nullable for the rest of the scope, removing the need for redundant optional chaining or non-null assertions.

It also supports printf-style positional tokens (%s, %d/%i, %j, %o) so error messages can be composed dynamically without template-literal noise, and its invariant.as() escape hatch lets callers throw a custom error class or factory function instead of the default InvariantError, making it easy to translate assertion failures into domain-specific exceptions like NetworkError or ValidationError.

What You Get

  • A type-safe invariant() function that narrows nullable values after a truthy check, thanks to a TypeScript asserts predicate signature
  • Positional message formatting with %s, %d/%i, %j, and %o tokens for dynamic error messages without string concatenation
  • An invariant.as() variant for throwing custom Error subclasses or factory-created errors instead of the built-in InvariantError
  • Automatic stack-trace cleanup so thrown errors point at the caller’s code, not outvariant’s internal frames
  • A dependency-free dual ESM/CJS build with bundled .d.ts type declarations

Common Use Cases

  • Validating fetched or parsed data before use, replacing manual optional chaining and non-null assertions
  • Converting failed preconditions into domain-specific error classes such as NetworkError or ValidationError
  • Asserting function arguments and configuration values at the start of a call with a formatted, positional-aware message
  • Standardizing scattered if/throw boilerplate across a codebase into a single consistent assertion pattern

Under The Hood

Architecture The package is a tiny two-module library: invariant.ts owns the public API (the invariant() assertion function, its .as() polymorphic-error variant, and the InvariantError class), while format.ts is a pure, side-effect-free string-formatting function that invariant.ts delegates to for positional substitution. index.ts is a thin barrel re-exporting both. Data flow is linear: a caller passes a predicate, message, and positionals to invariant(); on a falsy predicate it constructs an InvariantError, which calls format() to interpolate positionals and then strips outvariant’s own stack frames via cleanErrorStack. Because formatting is isolated behind one pure function, changing the positional token syntax touches only format.ts without altering the public call signature that consumers depend on.

Tech Stack Written in strict-mode TypeScript (tsconfig.json sets strict: true, targets ES6) and bundled with tsup (esbuild-based) into dual CommonJS/ESM output plus bundled .d.ts declarations, as configured in tsup.config.ts. It has zero runtime dependencies; devDependencies cover only build and test tooling (typescript, tsup, jest, ts-jest, ts-node) plus @ossjs/release for publishing. A GitHub Actions workflow (.github/workflows/main.yml) runs the test suite on push, and the package is published to npm as outvariant.

Code Quality Three Jest test files (invariant.test.ts, format.test.ts, InvariantError.test.ts, roughly 230 lines combined) exercise truthy/falsy predicate behavior, every positional token type, the .as() custom-error path including its Reflect.construct-with-factory-fallback branch, and stack-trace cleanup. Error handling is explicit and typed throughout (CustomErrorConstructor/CustomErrorFactory interfaces constrain what .as() accepts), and naming is small and consistent. No dedicated lint or formatter configuration is present in the repository, but CI runs the test suite on every push.

API Design The entire public surface is one function call: invariant(predicate, message, ...positionals) combines a null check, TypeScript type-narrowing, and a formatted throw into a single statement, replacing a typical multi-line if (!x) throw new Error(...) block. Positional tokens keep messages readable without template-literal noise, and invariant.as() lets consumers plug in their own error types with no extra ceremony. The README is a single, example-dense document covering every feature with runnable snippets, and getting started requires nothing beyond npm install outvariant and one import.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search