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-tsTable of Contents
- TypeGuard
- isType
- isOptional
- isMaybe
- isArray
- isLiteral
- isUnion
- isIntersection
- isRecord
- isPartialRecord
- isIndexRecord
- isLazy
- isTuple
- isEnum
- isSet
- isMap
- isInstanceof
- isRefine
- utility type guards
- generic types
- recursive types
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
satisfieskeyword on the result ofisLiteralwhen 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
satisfieskeyword on the result ofisUnion
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
satisfieskeyword on the result ofisIntersection
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
isRefinecan 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();
},
});