npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@moojo/misc-constructs

v0.7.0

Published

Compact TypeScript utilities that improve type-safety for error handling and exhaustiveness checking

Readme

@moojo/misc-constructs

npm version CI License: MIT

TypeScript utilities for type-safe error handling and exhaustiveness checking.

Installation

npm install @moojo/misc-constructs

API

Exhaustive Handling of String Unions

shouldNeverHappen

Compile-time exhaustiveness checking for union types. Place at the end of conditional chains to ensure all cases are handled.

import { shouldNeverHappen } from '@moojo/misc-constructs'

type Status = 'pending' | 'success' | 'failure'

function handleStatus(status: Status) {
  if (status === 'pending') return 'Waiting...'
  if (status === 'success') return 'Done!'
  if (status === 'failure') return 'Failed'
  shouldNeverHappen(status) // Compile error if a case is missing
}

When you add a new variant to a union type, the compiler won't warn you about all the switch statements and if-chains that need updating. Code silently falls through, and bugs appear at runtime. By placing shouldNeverHappen(x) at the end of your conditional logic, TypeScript will produce a compile error whenever a case is unhandled. This turns a runtime bug into a build-time error, making union type refactoring safe.

switchOn

Type-safe pattern matching. Compiler enforces that all cases are present and no extra cases exist.

import { switchOn } from '@moojo/misc-constructs'

type Op = '+' | '-' | '*'

function calculate(op: Op, a: number, b: number): number {
  return switchOn(op, {
    '+': () => a + b,
    '-': () => a - b,
    '*': () => a * b,
  })
}

TypeScript's switch statement doesn't enforce exhaustiveness by default, and it's a statement rather than an expression, so you can't use it inline. switchOn solves both problems: it's an expression that returns a value, and the compiler will reject code that has missing or extraneous cases. This makes it impossible to forget a case when the union type changes, and impossible to leave dead code behind when a case is removed.

Throwing Errors

failMe

Throws an error when evaluated. Useful as the right-hand side of ?? or || when a fallback value cannot be provided.

import { failMe } from '@moojo/misc-constructs'

// Environment variables
const port = process.env['PORT'] || failMe('PORT env variable is required')

// Nullish coalescing
const config = loadConfig() ?? failMe('config must be defined')

// After .find() - when the item must exist
const user = users.find(u => u.id === id) ?? failMe(`user not found: ${id}`)

// After .match() - when the pattern must match
const [_, commit] = input.match(/^@([0-9a-f]+)$/) ?? failMe(`bad commit hash: ${input}`)

// Ternary chains - when all branches must be covered
const handler = isAdmin ? adminHandler : isGuest ? guestHandler : failMe('unknown user type')

Compare with the traditional approach using throw:

// With failMe - concise, expression-based
const port = process.env['PORT'] || failMe('PORT is required')

// Without failMe - verbose, statement-based
const port = process.env['PORT']
if (!port) {
  throw new Error('PORT is required')
}

// With failMe - stays in expression context
const user = users.find(u => u.id === id) ?? failMe(`not found: ${id}`)

// Without failMe - breaks the flow
const user = users.find(u => u.id === id)
if (!user) {
  throw new Error(`not found: ${id}`)
}

failMe is a natural fit for teams that follow the fail-early-and-loud philosophy. When a precondition is violated, you want the code to fail immediately with a clear message, not silently continue with undefined or a default value that masks the real problem. failMe makes these assertions visible and keeps them inline with the code that depends on them.

Handling Errors

errorLike

Safely extracts error properties from caught values. Avoids unsafe type assertions on unknown.

import { errorLike } from '@moojo/misc-constructs'

try {
  await riskyOperation()
} catch (e) {
  // Without fallback - message may be undefined
  const { message, stack } = errorLike(e)

  // With fallback - message is guaranteed to be a string
  const err = errorLike(e, 'Operation failed')
  console.error(err.message) // string, never undefined
}

In JavaScript, anything can be thrown—not just Error instances, but strings, numbers, objects, or even undefined. TypeScript correctly types caught values as unknown, but this makes accessing .message or .stack awkward. The common workaround is (e as Error).message, but this is unsafe: if someone threw a string or a plain object, your code will misbehave. errorLike inspects the caught value at runtime and extracts properties only if they exist and are strings, giving you a consistent interface without lying to the type system.

catchMe / catchMeAsync

Captures execution results or errors in a structured format. Primarily for testing scenarios where you need to examine error properties.

import { catchMe, catchMeAsync } from '@moojo/misc-constructs'

// Synchronous
const result = catchMe(() => JSON.parse('{invalid}'))
if (!result.success) {
  console.log(result.error)      // The thrown value
  console.log(result.postmortem) // All enumerable properties of the error
}

// Asynchronous
const asyncResult = await catchMeAsync(async () => {
  return await fetchData()
})
if (asyncResult.success) {
  console.log(asyncResult.data)
}

In tests:

test('throws on invalid input', () => {
  expect(catchMe(() => parseConfig(''))).toMatchObject({
    success: false,
    postmortem: {
      message: expect.stringContaining('empty input'),
    },
  })
})

Jest's toThrow() matcher only lets you check the error message or type. But errors often carry additional properties—error codes, HTTP status codes, metadata—that you want to verify. catchMe captures the thrown value and extracts all its enumerable properties into a postmortem object, which you can then assert against with toMatchObject. This gives you fine-grained control over error assertions without resorting to try-catch blocks in every test.

License

MIT