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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@epic-web/invariant

v1.0.0

Published

Type safe utilities for throwing errors (and responses) if things aren't quite right. Inspired by npm.im/invariant

Downloads

60,394

Readme

npm install @epic-web/invariant

Build Status MIT License Code of Conduct

The Problem

Your application has boundaries. Network requests/responses, file system reads, etc. When you're working with these boundaries, you need to be able to handle errors that may occur, even in TypeScript.

TypeScript will typically make these boundaries much more obvious because it doesn't like not knowing what the type of something is. For example:

const formData = new FormData(formElement)
const name = await formData.get('name')
// name is `File | string | null`

Often it's a good idea to use a proper parsing library for situations like this, but for simple cases that can often feel like overkill. But you don't want to just ignore TypeScript because:

TypeScript is that brutally honest friend you put up with because they save you from making terrible mistakes. – @kentcdodds

So you check it:

const formData = new FormData(formElement)
const name = await formData.get('name')
// name is `File | string | null`
if (typeof name !== 'string') {
	throw new Error('Name must be a string')
}
// now name is `string` (and TypeScript knows it too)

You're fine throwing a descriptive error here because it's just very unlikely this will ever happen and even if it does you wouldn't really know what to do about it anyway.

It's not a big deal, but there's a tiny bit of boilerplate that would be nice to avoid. Especially when you find yourself doing this all over the codebase. This is the problem @epic-web/invariant solves.

The Solution

Here's the diff from what we had above:

const formData = new FormData(formElement)
const name = await formData.get('name')
// name is `File | string | null`
- if (typeof name !== 'string') {
- 	throw new Error('Name must be a string')
- }
+ invariant(typeof name === 'string', 'Name must be a string')
// now name is `string` (and TypeScript knows it too)

It's pretty simple. But honestly, it's nicer to read, it throws a special InvariantError object to distinguish it from other types of errors, and we have another useful utility for throwing Response objects instead of Error objects which is handy in Remix.

Usage

invariant

The invariant function is used to assert that a condition is true. If the condition is false, it throws an error with the provided message.

Basic Usage

import { invariant } from '@epic-web/invariant'

const creature = { name: 'Dragon', type: 'Fire' }
invariant(creature.name === 'Dragon', 'Creature must be a Dragon')

Throwing a Response on False Condition

import { invariant } from '@epic-web/invariant'

const creature = { name: 'Unicorn', type: 'Magic' }
invariant(creature.type === 'Fire', 'Creature must be of type Fire')
// Throws: InvariantError: Creature must be of type Fire

Using Callback for Error Message

import { invariantResponse } from '@epic-web/invariant'

const creature = { name: 'Elf', type: 'Forest' }
invariant(creature.type === 'Water', () => 'Creature must be of type Water')
// Throws: InvariantError: Creature must be of type Water

invariantResponse

The invariantResponse function works similarly to invariant, but instead of throwing an InvariantError, it throws a Response object.

Basic Usage

import { invariantResponse } from '@epic-web/invariant'

const creature = { name: 'Phoenix', type: 'Fire' }
invariantResponse(creature.type === 'Fire', 'Creature must be of type Fire')

Throwing a Response on False Condition

import { invariantResponse } from '@epic-web/invariant'

const creature = { name: 'Griffin', type: 'Air' }
invariantResponse(creature.type === 'Water', 'Creature must be of type Water')
// Throws: Response { status: 400, body: 'Creature must be of type Water' }

The response status default if 400 (Bad Request), but you'll find how to change that below.

Using Callback for Response Message

import { invariantResponse } from '@epic-web/invariant'

const creature = { name: 'Mermaid', type: 'Water' }
invariantResponse(
	creature.type === 'Land',
	() => `Expected a Land creature, but got a ${creature.type} creature`,
)

Throwing a Response with Additional Options

import { invariantResponse } from '@epic-web/invariant'

const creature = { name: 'Cerberus', type: 'Underworld' }
invariantResponse(
	creature.type === 'Sky',
	JSON.stringify({ error: 'Creature must be of type Sky' }),
	{ status: 500, headers: { 'Content-Type': 'text/json' } },
)

Differences from invariant

There are three main differences. With @epic-web/invariant:

  1. Error messages are the same in dev and prod
  2. It's typesafe
  3. We support the common case (for Remix anyway) of throwing Responses as well with invariantResponse.

License

MIT