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

typed-assert

v1.0.9

Published

typesafe assertion library for TypeScript 3.7+

Downloads

10,944,982

Readme

typed-assert

typed-assert is a typesafe assertion library implementing the TS 3.7 Assertion Functions API, without external dependencies.

See the documentation.

Consider using zod

While this library does a fine job for most simple use cases, please consider using zod if you need more complex assertions.

Install

npm install typed-assert

or

yarn add typed-assert

Why is it useful?

typed-assert promotes using unknown instead of any for "untrusted" values, e.g. user input, while still benefiting from incremental typing.

For example, JSON.stringify returns any, which is not typesafe. With typed-assert, we can instead treat the result as unknown and gradually check the contents at runtime and still get correct type inference:

import * as t from "typed-assert";

const parseConfigFile = (file: string): { readonly a: string, readonly b: number } => {
  const contents = JSON.parse(fs.readFileSync(file, { encoding: 'utf8'})) as unknown;
  // contents is "unknown" instead of any, because we don't trust the input yet
  t.isRecord(contents);
  // contents is "Record<string, unknown>"
  t.isString(contents.a);
  // contents.a is "string"
  t.isNumber(contents.b):
  // contents.b is "number";
  return {
    a: contents.a,
    b: contents.b,
  }; // correctly typed
}

How is it different from chai, jest.expect, etc?

typed-assert is both a compile-time and runtime assert library. It leverages the assertion function feature of TypeScript to help the typechecker narrow the inferred types. In many cases, this significantly reduces the need to use any, and promotes using unknown instead.

For example:

const u: unknown = {
  a: "value",
  b: 12,
};

chai.assert.typeOf(u, "object");
// u is still "unknown"
chai.assert.isNotNull(u);
// u is still "unknown"
chai.assert.typeof(u.a, "string");
// TS Error (ts2571): u is "unknown"

import * as t from "typed-assert";

t.isRecord(u);
// u is Record<string, unknown>
t.isString(u.a);
// u.a is string
t.isNumber(u.b);
// u.b is number

const v: { a: string; b: number } = u;
// no need to us `as ...`

Usage

typed-assert comes with a set of common assertions, as well as assertion combinators and utilities.

See the documentation for a full reference.

import * as t from "typed-assert";

// Base asserts
t.isExactly("a", "a");
t.isNotUndefined(null);
t.isNotNull(undefined);

// Asserts combinators
t.isOneOf("b", ["a", "b", "c"]);
t.isArrayOf([2, 3, 4], t.isNumber);

// Custom composite checks
interface ICustomType {
  readonly a: {
    readonly b: "c";
    readonly d: string;
  };
  readonly f?: number;
}

function assertCustomType(input: unknown): asserts input is ICustomType {
  t.isRecordWithKeys(input, ["a", "f"]);
  t.isRecordWithKeys(input.a, ["b", "d"]);
  t.isExactly(input.a.b, "c");
  t.isString(input.a.d);
  t.isOption(input.f, t.isNumber);
}

const v = {
  a: {
    b: "c",
    d: "",
  },
};
assertCustomType(v);

This library also comes with a combinator to transform an assertion functions into a type guard function:

const checkNumber = t.check(t.isNumber);
checkNumber(1) === true;
checkNumber("") === false;

It is especially convenient when combined with functional operations such as Array#filter:

const t = ["a", 3, "c", 4, null, 2]
  .filter(t.check(t.isNumber))
  .map(x => x % 2 === 0 ? x : null) // x: number
  .filter(t.check(t.isNotNull));
// t: number[] = [4, 2]

To encourage using asserts when dealing with untrusted JSON input, the following function is also exported:

export const safeJsonParse = (json: string): unknown =>
  JSON.parse(json) as unknown;

Configuration

This library is designed to work in the browser as well as in Node without external dependencies, and by default does not use the assert module from the Node stdlib, so it ships with a very basic assert implementation:

export type WeakAssert = (input: unknown, message?: string) => void;

export const defaultAssert: WeakAssert = (condition, message) => {
  if (!condition) {
    throw new TypeError(message);
  }
};

It is however possible to configure the library to use a provided base assert function, such as the native assert module:

import * as t from "typed-assert";
import nodeAssert from "assert";

t.setBaseAssert(nodeAssert);

Caveats

Due to limitations in the typechecker, there are syntactic restrictions in how to define and use type assertion functions. For example, you can not dynamically define an assertion function, even if it looks like a static definition.

Thus the following code won't compile:

function createIsExactly<T>(value: T): (input: unknown) => asserts input is T {
  return function isExactly(input: unknown): asserts input is T {
    t.isExactly(input, value);
  };
}
// No problem so far

createIsExactly("a")(null);
// Won't compile:
// Assertions require the call target to be an
//  identifier or qualified name.ts(2776)

For similar reasons, it is not possible to use type-inferred arrow functions to define assertion functions:

const isExactlyNull = (input: unknown): asserts input is null => assert(input === value);
// No problem so far

isExactlyNull("a", null):
// Won't compile:
// Assertions require the call target to be an
//  identifier or qualified name.ts(2776)

It is however possible to use arrow function with explicit typing of the left-hand operand:

const isExactlyNull: (input: unknown) => asserts input is null = (input) =>
  assert(input === null);

isExactlyNull("a");
// No problem

To simplify the implementation,

To simplify this pattern, this library also exports the Assert<Input, Output> type as defined below:

export type Assert<T> = (
  input: unknown,
  message?: string,
) => asserts input is T;

const isExactlyNull: Assert<null> = (input) => assert(input === null);

isExactlyNull("a");
// No problem

For convenience, this library also exports the following types, used internally:

export type WeakAssert = (input: unknown, message?: string) => void;

export type SubType<Input, Output> = Output extends Input ? Output : never;

export type Assert<Input = unknown, Output = Input> = (
  input: Input,
  message?: string,
) => asserts input is SubType<Input, Output>;

export type Check<Input = unknown, Output = Input> = (
  input: Input,
) => input is SubType<Input, Output>;

This way we can write:

const isExactlyNull: Assert<unknown, null> = (input) =>
  assert(input === null);

isExactlyNull("a");