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

typanic

v1.0.9

Published

Forced runtime type assertions for untrusted input — get the type you expect or throw, instead of silently coercing a wrong value to "" / 0 / false.

Readme

typanic

type + panic — get the type you expect, or it throws.

Tiny, dependency-free runtime type assertions for untrusted input (request and response bodies, webhook payloads, message/command payloads, parsed config, route/query params). Instead of silently coercing a wrong value into "", 0, or false — which produces noise and hides a real producer bug far from where it happens — typanic either gives you the value as the type you asked for, or throws a TypeError.

// ❌ silent, noisy, swallows malformed data
const name = typeof payload.name === "string" ? payload.name : ""

// ✅ loud and clean
import {forcedString} from "typanic"
const name = forcedString(payload.name, "name") // throws if it isn't a string

For values that are already typed/validated (a generated model accessor, a boundary-typed param) you don't need this at all — just use them directly. typanic is for the boundary where data is genuinely untrusted.

Install

npm install typanic

ESM only. Ships with TypeScript declarations (.d.ts) generated from JSDoc.

API

Every helper takes the value and an optional label used in the error message (Expected <label> to be a <type> but got <actual>).

Required — throw when the value is the wrong type

| Function | Returns | Notes | | --- | --- | --- | | forcedString(value, label?) | string | throws unless typeof value === "string" | | forcedOneOf(value, allowedValues, label?) | T | throws unless value is one of allowedValues (enum / one-of) | | forcedInteger(value, label?) | number | accepts integers and integer-looking strings ("42") | | forcedIntegerFromString(value, label?) | number | accepts safe decimal integer strings only; useful for form/query values | | forcedPositiveInteger(value, label?) | number | accepts safe integers greater than zero and integer-looking strings ("42") | | forcedPositiveIntegerFromString(value, label?) | number | accepts safe positive decimal integer strings only | | forcedStringParam(params, key, label?) | string | reads a required single string from a route/query param map | | forcedNonBlankStringParam(params, key, label?) | string | reads a required trimmed non-blank string from a param map | | forcedPositiveIntegerParam(params, key, label?) | number | reads a required safe positive decimal integer from a string param map | | forcedPositiveIntegerList(value, label?) | number[] | parses a scalar or array list of safe positive integers | | forcedNonBlankString(value, label?) | string | trims and rejects blank strings | | forcedFloat(value, label?) | number | accepts finite numbers and numeric strings; rejects NaN/Infinity | | forcedBoolean(value, label?) | boolean | does not coerce "true"/1 — pass a real boolean | | forcedFunction(value, label?) | Function | throws unless typeof value === "function" | | forcedError(value, label?) | Error | throws unless value instanceof Error |

Error conversion

Use ensureError(value, label?) at catch/throw boundaries where JavaScript can throw strings, numbers, or other non-Error values. Error instances are returned unchanged. Non-Error values are wrapped in a new Error and preserved as error.cause.

Use errorMessage(value, label?) when a UI or status field needs a displayable message for a caught value. String throws are already messages and are returned unchanged; everything else is normalized through ensureError.

import {errorMessage} from "typanic"

try {
  await work()
} catch (error) {
  const message = errorMessage(error, "work error")
  showError(message)
}

Optional — null when absent, throw when present-but-wrong-typed

| Function | Returns | | --- | --- | | optionalString(value, label?) | string \| null | | optionalOneOf(value, allowedValues, label?) | T \| null | | optionalInteger(value, label?) | number \| null | | optionalIntegerFromString(value, label?) | number \| null | | optionalPositiveInteger(value, label?) | number \| null | | optionalPositiveIntegerFromString(value, label?) | number \| null | | optionalStringParam(params, key, label?) | string \| null | | optionalNonBlankStringParam(params, key, label?) | string \| null | | optionalPositiveIntegerParam(params, key, label?) | number \| null | | optionalPositiveIntegerList(value, label?) | number[] \| null | | optionalNonBlankString(value, label?) | string \| null | | optionalFloat(value, label?) | number \| null | | optionalBoolean(value, label?) | boolean \| null | | optionalFunction(value, label?) | Function \| null |

null and undefined both count as "absent" and return null. A value that is present but of the wrong type still throws — absence and corruption are different things.

import {forcedPositiveIntegerFromString, optionalString} from "typanic"

const cols = forcedPositiveIntegerFromString(payload.cols, "cols") // positive decimal string -> number, or throws
const cursor = optionalString(payload.cursor, "cursor")            // string | null
const status = optionalString(payload.status, "status") ?? "ok"    // default only when you truly need one

Param-map helpers are for route/query frameworks that expose values as Record<string, string | string[] | undefined>. They reject repeated single-value params instead of picking the first value silently.

import {forcedPositiveIntegerParam, optionalPositiveIntegerList} from "typanic"

const projectId = forcedPositiveIntegerParam(routeParams, "project_id", "Project ID")
const userIds = optionalPositiveIntegerList(routeParams.user_ids, "User IDs") ?? []

Why "forced"?

The value is forced to be the type you declared. There is no quiet fallback: the data is what you expect, or your code stops at the boundary with a clear error instead of carrying a silent empty string into the rest of the system.

License

MIT