JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 33
  • Score
    100M100P100Q76989F
  • License ISC

a typescript library for building type guards quickly while maintaining type safety

Package Exports

  • isguard-ts
  • isguard-ts/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (isguard-ts) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

isguard-ts

A powerful typescript library that helps you build type guards quickly while maintaining type safety.

isguard-ts utilizes the typescript compiler to ensure that its type guards are aligned with the guarded type.
For example, when making a change to your type, isguard-ts will inform you to update your type guard as well.

Installation

npm install isguard-ts

Table of Contents

Basic Usage

TypeGuard<T>

The most basic type - represents a type guard of T

type TypeGuard<T> = (value: unknown) => value is T;

isType

Helps you create type guards for types and interfaces

Best Practice: Pass the generic type argument into isType
Otherwise optional fields might have an unexpected behavior

type Person = {
    name: string;
    age: number;
};

const isPerson = isType<Person>({
    name: isString,
    age: isNumber,
});

isPerson({ name: "Hello", age: 6 }); // true

isOptional

Helps you create type guards for optional (potentially undefined) types

isOptional(isNumber); // or isNumber.optional();

isMaybe

Helps you create type guards for nullable (potentially null) types

isMaybe(isNumber); // or isNumber.maybe();

isArray

Helps you create type guards for arrays

isArray(isBoolean); // or isBoolean.array();

isLiteral

Helps you create type guards for literals

const isHello = isLiteral("Hello");
const is12 = isLiteral(12);

isLiteral can receive multiple values

Best Practice: Use the satisfies keyword on the result of isLiteral when passing multiple values
This ensures the result is of the expected type

const directions = ["up", "down", "left", "right"] as const;
type Direction = (typeof directions)[number];

const isDirection = isLiteral(...directions) satisfies TypeGuard<Direction>;

isUnion

Helps you create type guards for unions

Best Practice: Use the satisfies keyword on the result of isUnion
This ensures the result is of the expected type

type A = { a: number };
type B = { b: string };
type C = A | B;

const isA = isType<A>({ a: isNumber });
const isB = isType<B>({ b: isString });

isUnion(isA, isB) satisfies TypeGuard<C>; // or isA.or(isB);

isIntersection

Helps you create type guards for intersections

Best Practice: Use the satisfies keyword on the result of isIntersection
This ensures the result is of the expected type

type A = { a: number };
type B = { b: string };
type C = A & B;

const isA = isType<A>({ a: isNumber });
const isB = isType<B>({ b: isString });

isIntersection(isA, isB) satisfies TypeGuard<C>; // or isA.and(isB);

isRecord

Helps you create type guards for records

const timeUnits = ["second", "minute", "hour"] as const;
type TimeUnit = (typeof timeUnits)[number];

isRecord(timeUnits, isNumber);
// Record<TimeUnit, number>

isPartialRecord

Works just like isRecord but allows for undefined values

const timeUnits = ["second", "minute", "hour"] as const;
type TimeUnit = (typeof timeUnits)[number];

isPartialRecord(timeUnits, isNumber);
// Partial<Record<TimeUnit, number>>

isIndexRecord

Works just like isRecord but checks only the values and not the keys

isIndexRecord(isNumber); // or isNumber.indexRecord();
// Record<PropertyKey, number>

isLazy

Helps you lazy load a type guard. Useful for:

  • Resolving undefined errors due to circular imports
  • Creating type guards for recursive types
import { isPerson } from "./some-module";

const isPeople = isLazy(() => isPerson).array();

In the example above isPerson, imported from ./some-module, might be undefined when isPeople is being created, due to circular imports. So isPerson.array() would throw an error. isLazy solves this issue by accessing isPerson only when needed.

isTuple

Helps you create type guards for tuples

Best Practice: Pass the generic type argument into isTuple
Otherwise optional fields might have an unexpected behavior

type Row = [number, string?];

const isRow = isTuple<Row>([isNumber, isString.optional()]);

isRow([6, "Hello"]); // true
isRow([6]); // true
isRow(["Hello", "Bye"]); // false

isEnum

Helps you create type guards for enums

enum Direction {
    up = 0,
    down = 1,
    left = 2,
    right = 3,
}

const isDirection = isEnum(Direction);

isDirection(Direction.up); // true
isDirection(2); // true
isDirection("hello"); // false

isSet

Helps you create type guards for sets

isSet(isNumber); // or isNumber.set();
// Set<number>

isMap

Helps you create type guards for maps

isMap(isString, isBoolean);
// Map<string, boolean>

isInstanceof

Helps you create type guards for classes

abstract class Animal { }
class Dog extends Animal { }

const isAnimal = isInstanceof(Animal);
const isDog = isInstanceof(Dog);

isRefine

Helps you refine existing type guards. Can be used for:

  • Branded types (like Email, PositiveNumber and more)
  • Template literals (like `Bye ${string}`)

Warning: using isRefine can be unsafe because it let's you implement potentially false logic
Use at your own risk.

type Farewell = `Bye ${string}`;

const isFarewell = isRefine(isString, (value: string): value is Farewell => {
    return value.startsWith("Bye ");
});

Built-in Utility Type Guards

const isNumber: TypeGuard<number>;
const isBigint: TypeGuard<bigint>;
const isString: TypeGuard<string>;
const isBoolean: TypeGuard<boolean>;
const isSymbol: TypeGuard<symbol>;
const isFunction: TypeGuard<Function>;
const isPropertyKey: TypeGuard<PropertyKey>;

const isDate: TypeGuard<Date>;
const isRegExp: TypeGuard<RegExp>;
const isObject: TypeGuard<object>;
const isError: TypeGuard<Error>;
const isEvalError: TypeGuard<EvalError>;
const isRangeError: TypeGuard<RangeError>;
const isReferenceError: TypeGuard<ReferenceError>;
const isSyntaxError: TypeGuard<SyntaxError>;
const isTypeError: TypeGuard<TypeError>;
const isURIError: TypeGuard<URIError>;

const isNull: TypeGuard<null>;
const isUndefined: TypeGuard<undefined>;
const isNil: TypeGuard<null | undefined>;

const isTrue: TypeGuard<true>;
const isFalse: TypeGuard<false>;

const isUnknown: TypeGuard<unknown>;
const isNever: TypeGuard<never>;

Advanced Usage

Generic Types

When creating type guards for generic types, you need to create your own TypeGuard generator

type ValueHolder<T> = {
    value: T;
};

const isValueHolder = <T>(isValue: TypeGuard<T>) => {
    return isType<ValueHolder<T>>({
        value: isValue,
    });
};

const isNumberHolder = isValueHolder(isNumber);

Recursive Types

Important: Annotate the recursive guard to avoid typescript errors

One way to build recursive type guards is by using isLazy

type Json =
    number |
    string |
    boolean |
    null |
    Json[] |
    { [key: string]: Json; };

const isJson: TypeGuard<Json> = isUnion(
    isNumber,
    isString,
    isBoolean,
    isNull,
    isLazy(() => isArray(isJson)),
    isLazy(() => isJson),
);
type Tree = {
    value: number;
    left?: Tree;
    right?: Tree;
};

const isTree: TypeGuard<Tree> = isType<Tree>({
    value: isNumber,
    left: isLazy(() => isTree).optional(),
    right: isLazy(() => isTree).optional(),
});

Another way (though less recommended) is by using a getter

const isTree: TypeGuard<Tree> = isType<Tree>({
    value: isNumber,
    get left() {
        return isTree.optional();
    },
    get right() {
        return isTree.optional();
    },
});