Type Guard Kit
A TypeScript module to help create a type and its guards from a single source definition, to catch unexpected, incompatible types entering your application due to:
- interface definitions being outdated without versioning protection from breaking changes.
- interfaces not being adhered to.
- data being corrupted.
Setup
Deno module URL
https://deno.land/x/typeguardkit/mod.tsnpm installation
npm install typeguardkitUsage
Example
import {
_boolean,
_null,
_number,
_string,
arrayOf,
Asserter,
objectAsserter,
unionOf,
} from "./mod.ts";
// import from "typeguardkit" if using npm
// entity_types/book.ts
const asserter = objectAsserter("Book", {
title: _string,
authors: arrayOf(_string),
pageCount: _number,
rating: unionOf(_number, _null),
isRecommended: _boolean,
});
export interface Book extends ReturnType<typeof asserter> {}
export const _Book: Asserter<Book> = asserter;
// api/get_book.ts
export async function getBook(id: string): Promise<Book> {
const response = await fetch(`/api/books/${id}`);
const responseBody = await response.json();
// If `responseBody` is a `Book`, `_Book` returns `responseBody` as `Book`.
// Otherwise, `_Book` throws a `TypeAssertionError`, including the optional
// value name `"responseBody"` in its `message`.
return _Book(responseBody, "responseBody");
}Asserters
An Asserter is a type assertion function.
interface Asserter<Type> {
(value: unknown, name?: string): Type;
readonly typeName: string;
}If value is of Type, the Asserter should return value as Type.
Otherwise, the Asserter should throw a TypeAssertionError.
The module includes the _boolean, _number, and _string Asserters.
It also includes the _null and _undefined Asserters, which can be used to
create union type Asserters with the unionOf function.
As well as wrapping Asserters in the
assertIs or
is functions, you can use tbem like this:
import { _string } from "./mod.ts";
// import from "typeguardkit" if using npm
function handleUnknown(x: unknown) {
let y;
try {
y = _string(x, "x");
} catch {
return;
}
// `y` has now been initialized and inferred to be of type `string`, so can be
// passed to `handleString`. `x` is still of `unknown` type though, so cannot.
handleString(y);
}
function handleString(y: string) {}You can create your own Asserters with the typeAsserter function. For
example, the _string Asserter was created like this:
import { typeAsserter } from "./mod.ts";
// import from "typeguardkit" if using npm
export const _string = typeAsserter(
"string",
(value): value is string => typeof value === "string",
);Prefixing Asserter names with an underscore _ helps to avoid name conflicts
and shadowing.
Assertion signature wrapper
The assertIs function wraps an Asserter<Type> with an assertion signature so
the value passed in can be narrowed to Type. If the Asserter throws an
error, it will bubble up. Otherwise, assertIs will not return a value, but
after calling it, the value passed in will be narrowed to Type.
You can use assertIs like this:
import { _string, assertIs } from "./mod.ts";
// import from "typeguardkit" if using npm
function handleUnknown(x: unknown) {
try {
assertIs(_string, x, "x");
} catch {
return;
}
// `x` has now been narrowed to type `string`, so can be passed to
// `handleString`.
handleString(x);
}
function handleString(x: string) {}Predicate signature wrapper
The is function wraps an Asserter<Type> with a predicate signature, creating
a type guard, so the value passed in can be narrowed to Type. If the
Asserter throws an error, is will catch it and return false. Otherwise,
is will return true.
You can use is like this:
import { _string, is } from "./mod.ts";
// import from "typeguardkit" if using npm
function handleUnknown(x: unknown) {
if (is(_string, x)) {
// `x` has now been narrowed to type `string`, so can be passed to
// `handleString`.
handleString(x);
}
}
function handleString(x: string) {}Literal union Asserters
You can create an Asserter for a literal union type using the
literalUnionAsserter function like this:
import { Asserter, is, literalUnionAsserter } from "./mod.ts";
// import from "typeguardkit" if using npm
export const directions = ["up", "right", "down", "left"] as const;
const asserter = literalUnionAsserter("Direction", directions);
export type Direction = typeof directions[number];
export const _Direction: Asserter<Direction> = asserter;
function handleUnknown(x: unknown) {
if (is(_Direction, x)) {
// `x` has now been narrowed to type `Direction`, so can be passed to
// `handleDirection`.
handleDirection(x);
}
}
function handleDirection(x: Direction) {}Other Asserter factory functions
The following functions create new Asserters from existing ones.
unionOf
unionOf returns an Asserter for the union of the Types of the provided
Asserters.
You can use unionOf like this:
import { _null, _string, is, unionOf } from "./mod.ts";
// import from "typeguardkit" if using npm
const _stringOrNull = unionOf(_string, _null);
function handleUnknown(x: unknown) {
if (is(_stringOrNull, x)) {
// `x` has now been narrowed to type `string | null`, so can be passed to
// `handleStringOrNull`.
handleStringOrNull(x);
}
}
function handleStringOrNull(x: string | null) {}arrayOf
arrayOf returns an Asserter<Array<Type>>, created using the provided
Asserter<Type>.
You can use arrayOf like this:
import { _string, arrayOf, is } from "./mod.ts";
// import from "typeguardkit" if using npm
const _arrayOfString = arrayOf(_string);
function handleUnknown(x: unknown) {
if (is(_arrayOfString, x)) {
// `x` has now been narrowed to type `Array<string>`, so can be passed to
// `handleArrayOfString`.
handleArrayOfString(x);
}
}
function handleArrayOfString(x: string[]) {}ObjectAsserters
An ObjectAsserter is an Asserter for the object type defined by its
propertyAsserters.
import { Asserter } from "./mod.ts";
// import from "typeguardkit" if using npm
interface ObjectAsserter<Type extends Record<string, unknown>>
extends Asserter<Type> {
readonly propertyAsserters: Readonly<
{ [Key in keyof Type]: Asserter<Type[Key]> }
>;
}You can create an ObjectAsserter with the objectAsserter function and use it
like this:
import { _number, _string, Asserter, is, objectAsserter } from "./mod.ts";
// import from "typeguardkit" if using npm
const asserter = objectAsserter("User", {
name: _string,
age: _number,
});
export interface User extends ReturnType<typeof asserter> {}
export const _User: Asserter<User> = asserter;
function handleUnknown(x: unknown) {
if (is(_User, x)) {
// `x` has now been narrowed to type `User`, so can be passed to
// `handleUser`.
handleUser(x);
}
}
function handleUser(x: User) {}