Lightweight, zero-dependency toolkit for building isFoo style type guards in TypeScript.
Runtime-safe π‘οΈ, composable π§©, and ergonomic β¨.
Tired of writing the same isFoo again and again?
Let is-kit do it for you:
- Less boilerplate
- Type-safe
- Composable
- Zero-dependency
β Grab a coffee, let
is-kitdo the boring work.
Node via npm
pnpm add is-kit
# or
bun add is-kit
# or
npm i is-kit
# or
yarn add is-kitESM and CJS builds are provided for npm consumers. Types are bundled.
// Deno/Bun/JSR-aware tooling
import { define, and, or } from 'jsr:@nyaomaru/is-kit';Build is functions from tiny, composable pieces:
import {
define,
and,
or,
not,
struct,
oneOfValues,
optional,
isNumber,
isString,
predicateToRefine,
} from 'is-kit';
// Define small guards
const isShortString = define<string>((v) => isString(v) && v.length <= 3);
const isPositive = and(
isNumber,
predicateToRefine<number>((v) => v > 0)
);
// Combine them (or / not)
const isShortOrPositive = or(isShortString, isPositive);
const isOther = not(isShortOrPositive);
// Define object shapes
const isRole = oneOfValues(['admin', 'member'] as const);
const isUser = struct({
id: isPositive, // number > 0
name: isString, // string
role: isRole, // 'admin' | 'member'
nickname: optional(isShortString), // string <= 3 | undefined
});
// Use them
isPositive(3); // true
isShortOrPositive('foo'); // true
isShortOrPositive(false); // false
isOther('x'); // true
const maybeUser: unknown = { id: 7, name: 'Rin', role: 'admin' };
if (isUser(maybeUser)) {
// The type is narrowed inside this block
maybeUser.role; // 'admin' | 'member'
maybeUser.nickname?.toUpperCase();
}Composed guards stay reusable:
isPositive can be used standalone or as part of a struct definition.
When validating complex shapes, reach for struct β and friends like arrayOf, recordOf, or oneOf.
Built-in primitives: isString, isNumber (finite), isBoolean, isBigInt, isSymbol, isUndefined, isNull β and a preset isPrimitive for any primitive.
import { isPrimitive, isNumber } from 'is-kit';
isPrimitive('x'); // true
isPrimitive(123); // true
isPrimitive(NaN); // true (use isNumber for finite only)
isPrimitive({}); // false- Define once:
define<T>(fn)turns a plain function into a type guard. - Upgrade predicates:
predicateToRefine(fn)adds narrowing. - Compose freely:
and,or,not,oneOf,arrayOf,struct⦠- Stay ergonomic: helpers like
nullable,optional,equals,safeParse,narrowKeyTo.
equalsBy preserves the base type and does not narrow selected fields to literals.
When you need to narrow a property to a specific literal value, use narrowKeyTo.
import { narrowKeyTo, or, struct } from 'is-kit';
// Base guard (e.g., via struct)
type User = { id: string; age: number; role: 'admin' | 'guest' | 'trial' };
const isUser = struct({
id: isString,
age: isNumber,
role: oneOfValues('admin', 'guest', 'trial'),
});
const byRole = narrowKeyTo(isUser, 'role');
const isGuest = byRole('guest'); // Readonly<User> & { role: 'guest' }
const isTrial = byRole('trial'); // Readonly<User> & { role: 'trial' }
const isGuestOrTrial = or(isGuest, isTrial);
declare const value: unknown;
if (isGuestOrTrial(value)) {
// value.role is 'guest' | 'trial'
}For full API details and runnable examples, visit
Requires Node 22 and pnpm 10.12.4 (via Corepack or mise).
Want to hack on is-kit?
See DEVELOPER.md for setup, scripts, and contribution workflow.
See CONTRIBUTE.md for workflow, commit style, and tool setup. π