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 🙏

© 2025 – Pkg Stats / Ryan Hefner

narrowland

v1.4.0

Published

A lightweight TypeScript library providing type-safe assertions, type guards, and error handling utilities.

Readme

🛡️ Narrowland

codecov npm version Bundle size License: MIT

A lightweight TypeScript library providing type guards, type assertions, and invariant utilities for runtime type narrowing.

🚀 30-Second Pitch

Narrowland solves the problem of runtime type narrowing in TypeScript applications. Instead of writing verbose type checks and assertions, you get a clean, consistent API that provides:

  • Type Guards (is.*) - Check types without throwing errors, perfect for conditional logic
  • Type Assertions (assert.*) - Throw errors for invalid types with full TypeScript inference
  • Invariant Utilities (ensure, invariant, raiseError) - Handle edge cases and invariants gracefully

All functions are tree-shakeable, zero-dependency, and fully typed with TypeScript's type narrowing. You can import individual functions or use the grouped APIs.

📦 Installation

npm install narrowland
# or
pnpm add narrowland
# or
yarn add narrowland

🎯 Quick Start

// Import individual functions (tree-shakeable)
import { isString, assertNumber, ensure, invariant } from 'narrowland'

// Or import grouped APIs
import { is, assert, ensure, invariant } from 'narrowland'

// Type guards - return boolean, don't throw
if (isString(userInput)) {
  // userInput is now typed as string
  console.log(userInput.toUpperCase())
}

// Type guards from grouped import
if (is.string(userInput)) {
  // userInput is now typed as string
  console.log(userInput.toUpperCase())
}

// Assertions - throw on invalid types
assertNumber(age) // throws if not a number
// age is now typed as number

// Assertions from grouped import
assert.number(age) // throws if not a number
// age is now typed as number

// Ensure - return value or throw (only for null/undefined)
const name = ensure(user.name, 'Name is required')
// name is typed as NonNullable<typeof user.name>

// Invariant - check conditions (throws on falsy, does nothing on truthy)
invariant(isString(items), 'Items must be a string')

📚 API Reference

All functions except is.* throw errors if the condition is not satisfied.

Type Guards (is.*)

Type guards return boolean and narrow types without throwing errors. Safer than assertions because they don't throw.

| Function | Type Guard | Description | |----------|------------|-------------| | is.defined(value) | value is NonNullable<T> | Checks if value is not null or undefined | | is.notNull(value) | value is Exclude<T, null> | Checks if value is not null | | is.truthy(value) | value is Exclude<T, false \| 0 \| '' \| null \| undefined> | Checks if value is truthy | | is.falsy(value) | value is Extract<T, false \| 0 \| '' \| null \| undefined> | Checks if value is falsy | | is.string(value) | value is string | Checks if value is a string | | is.nonEmptyString(value) | value is string | Checks if value is a non-empty string | | is.number(value) | value is number | Checks if value is a finite number | | is.boolean(value) | value is boolean | Checks if value is a boolean | | is.bigint(value) | value is bigint | Checks if value is a bigint | | is.symbol(value) | value is symbol | Checks if value is a symbol | | is.instanceOf(value, constructor) | value is T | Checks if value is an instance of the given constructor | | is.array(value) | value is T[] | Checks if value is an array | | is.nonEmptyArray(value) | value is [T, ...T[]] | Checks if value is a non-empty array | | is.arrayOf(value, guard) | value is T[] | Checks if value is an array whose items satisfy the provided guard | | is.stringLiteral(value, literals) ⚠️ | value is T[number] | Checks if value is a member of string literals array (deprecated: use is.oneOf instead, will be removed in v2.0.0) | | is.oneOf(value, collection) | value is T[number] | Checks if value is a member of the provided collection (works with any type) | | is.object(value) | value is T | Checks if value is a plain object | | is.propertyOf(key, predicate) | obj is T & { [P in K]-?: U } | Checks that the selected property satisfies the provided predicate and makes it required | | is.keyOf(value, record) | value is T & keyof U | Checks if value is a key of the provided record |

Type Assertions (assert.*)

Assertions throw errors for invalid types and narrow types in the same scope. Use when you expect the value to be valid.

| Function | Assertion | Description | |----------|-----------|-------------| | assert.defined(value, message?) | asserts value is NonNullable<T> | Throws if value is null or undefined | | assert.notNull(value, message?) | asserts value is Exclude<T, null> | Throws if value is null | | assert.truthy(value, message?) | asserts value is Exclude<T, false \| 0 \| '' \| null \| undefined> | Throws if value is falsy | | assert.falsy(value, message?) | asserts value is Extract<T, false \| 0 \| '' \| null \| undefined> | Throws if value is truthy | | assert.string(value, message?) | asserts value is string | Throws if value is not a string | | assert.nonEmptyString(value, message?) | asserts value is string | Throws if value is not a non-empty string | | assert.number(value, message?) | asserts value is number | Throws if value is not a finite number | | assert.boolean(value, message?) | asserts value is boolean | Throws if value is not a boolean | | assert.bigint(value, message?) | asserts value is bigint | Throws if value is not a bigint | | assert.symbol(value, message?) | asserts value is symbol | Throws if value is not a symbol | | assert.instanceOf(value, constructor, message?) | asserts value is T | Throws if value is not an instance of the given constructor | | assert.array(value, message?) | asserts value is T[] | Throws if value is not an array | | assert.nonEmptyArray(value, message?) | asserts value is [T, ...T[]] | Throws if value is not a non-empty array | | assert.arrayOf(value, guard, message?) | asserts value is T[] | Throws if value is not an array whose items satisfy the provided guard | | assert.stringLiteral(value, literals, message?) ⚠️ | value is T[number] | Throws if value is not a member of provided string literals array (deprecated: use assert.oneOf instead, will be removed in v2.0.0) | | assert.oneOf(value, collection, message?) | asserts value is T[number] | Throws if value is not a member of the provided collection (works with any type) | | assert.object(value, message?) | asserts value is T | Throws if value is not a plain object | | assert.keyOf(value, record, message?) | asserts value is T & keyof U | Throws if value is not a key of the provided record | | assert.fromPredicate(predicate, message?) | (value, message?) => asserts value is T | Creates custom assertion from predicate |

Invariant Utilities

| Function | Return Type | Description | |----------|-------------|-------------| | ensure<T>(value, message?) | NonNullable<T> | Returns value or throws if null/undefined (only checks nullish) | | invariant(condition, message?) | asserts condition | Throws if condition is falsy (generic condition checker) | | raiseError(message, options?) | never | Throws error with custom name/code/cause (most flexible) |

🎨 When to Use What?

Narrowland provides a palette of solutions from most generic to very specific:

Type Guards (is.*) - Safest, Most Flexible

  • Use when: You want to check types without throwing
  • Best for: Conditional logic, filtering, optional validation
  • Example: if (isString(value)) { /* value is string */ }

Type Assertions (assert.*) - Most Direct

  • Use when: You expect the value to be valid and want to fail fast
  • Best for: Input validation, API boundaries, when you're confident about the type
  • Example: assertString(userInput) - throws immediately if not string

Ensure - Specific Null/Undefined Check

  • Use when: You have a maybe value and know it should be defined
  • Best for: Optional chaining results, API responses, configuration values
  • Throws: If value is null or undefined (similar to invariant, but only for nullish values)
  • Example: const name = ensure(user.name) - only checks for null/undefined

Invariant - Generic Condition Checker

  • Use when: You want to check any condition, not just types
  • Best for: Business logic validation, state checks, complex conditions
  • Example: invariant(user.age >= 18, 'User must be adult')

RaiseError - Most Flexible Error Creation

  • Use when: You need custom error types with specific properties or inline assertions
  • Best for: API errors, domain-specific errors, when you need error codes, inline assertions
  • Example: getUserById(maybeId ?? raiseError('User id must be defined'))

🍳 Copy-Paste Recipes

Type Guards - Simple Checks

import { isDefined, isTruthy } from 'narrowland'

// Check if value exists
if (isDefined(maybeValue)) {
  // maybeValue is now NonNullable<typeof maybeValue>
  console.log(maybeValue)
}

// Check if value is truthy
if (isTruthy(userInput)) {
  // userInput is now truthy type
  return userInput
}

Assertions - Fail Fast

import { assertString } from 'narrowland'

function processInput(input: unknown) {
  assertString(input) // throws if not string
  // input is now string
  return input.toUpperCase()
}

Ensure - Handle Null/Undefined

import { ensure } from 'narrowland'

function getUserName(user: { name?: string | null }) {
  // ensure throws if null/undefined, returns value otherwise
  const name = ensure(user.name, 'Name is required')
  // name is now string (was string | null | undefined)
  return name
}

Invariant - Business Rules

import { invariant } from 'narrowland'

function calculateAge(birthYear: number) {
  invariant(birthYear > 1900, 'Invalid birth year')
  invariant(birthYear <= new Date().getFullYear(), 'Birth year cannot be in future')
  
  return new Date().getFullYear() - birthYear
}

Filtering Arrays

import { isString, isInstanceOf } from 'narrowland'

// Filter strings from mixed array
const mixedArray = ['hello', 42, 'world', true]
const strings = mixedArray.filter(isString) // TypeScript knows these are strings

// Filter Date instances from mixed array
const values = [new Date(), '2023-01-01', new Date('2024-01-01'), null, 42]
const dates = values.filter((v) => isInstanceOf(v, Date)) // TypeScript knows `dates` is Date[]

Property Guards - Filter Objects by Required Keys

import { isDefined, isPropertyOf } from 'narrowland'

type User = {
  id: string
  email?: string | null
}

const users: User[] = [
  { id: '1', email: '[email protected]' },
  { id: '2', email: null },
  { id: '3' },
]

const hasEmail = isPropertyOf('email', isDefined)

const contactableUsers = users.filter(hasEmail)
// ?^ contactableUsers is typed as PropNarrow<User, "email", {}>[]
contactableUsers.forEach((user) => {
  user.email.toLowerCase()
  // ?^ user.email is typed as string
})

ArrayOf - Array Type Narrowing

import { isArrayOf, isString } from 'narrowland'

// Narrow array of strings
const maybeStringArray: unknown = ['hello', 'world', 'typescript']
if (isArrayOf(maybeStringArray, isString)) {
  // maybeStringArray is now typed as string[]
  const uppercased = maybeStringArray.map((s) => s.toUpperCase())
}

OneOf - Union Type Narrowing

import { isOneOf, assertOneOf } from 'narrowland'

// Works with any type, not just strings
const status = 'pending' as unknown
const validStatuses = ['pending', 'completed', 'failed'] as const

// Type guard - returns boolean
if (isOneOf(status, validStatuses)) {
  // status is now typed as 'pending' | 'completed' | 'failed'
  console.log(status.toUpperCase())
}

// Assertion - throws if invalid
assertOneOf(status, validStatuses)
// status is now typed as 'pending' | 'completed' | 'failed'

// Works with mixed types too
const value = 42 as unknown
const allowedValues = [42, 'hello', true, null] as const

if (isOneOf(value, allowedValues)) {
  // value is now typed as 42 | 'hello' | true | null
}

KeyOf - Safe Object Key Access

import { isKeyOf, assertKeyOf } from 'narrowland'

const handlers = {
  click: () => 'clicked',
  submit: () => 'submitted',
} as const

// Type guard - returns boolean
const eventName = 'click' as string
if (isKeyOf(eventName, handlers)) {
  // eventName is now typed as 'click' | 'submit'
  handlers[eventName]() // Safe to call
}

// Assertion - throws if invalid
assertKeyOf(eventName, handlers)
// eventName is now typed as 'click' | 'submit'
handlers[eventName]() // Safe to call

📊 Bundle Size

  • Size: 902 B (minified + brotli)
  • Dependencies: 0
  • Tree-shakeable: ✅ (import individual functions)
  • ESM + CJS: ✅

🛠️ Development

Prerequisites

  • Node.js 20+
  • pnpm 8+

Setup

# Clone the repository
git clone https://github.com/your-username/narrowland.git
cd narrowland

# Install dependencies
pnpm install

# Build the library
pnpm build

# Run in development mode
pnpm dev

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git switch -c feat/amazing-feature)
  3. Commit your changes (git commit -m 'feat: add some amazing feature')
  4. Push to the branch (git push origin feat/amazing-feature)
  5. Open a Pull Request

Development Guidelines

  • Add tests for new functionality
  • Update documentation as needed

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Inspired by tiny-invariant for the invariant pattern
  • Built with modern tooling: Vitest, Biome, RSLib
  • Zero dependencies for maximum compatibility

Made with ❤️ for the TypeScript community