power-guard
v1.1.0
Published
The 0 dependency recipe for type strong validations: from `unknown` to anything you want. [中文](README-cn.md)
Downloads
375
Readme
Power Guard
The 0 dependency recipe for type strong validations: from unknown
to anything you want. 中文
🤔 A Cozy Way to Validate Unknown Variables
Instead of having:
const validate = (x: unknown): SomeType => {
if (!(typeof x === 'object' && x !== null)) {
throw new Error('x should be an object');
}
if (!('foo' in x && typeof x.foo === 'string')) {
throw new Error('x.foo should be a string');
}
if (!('bar' in x && typeof x.bar === 'boolean')) {
throw new Error('x.bar should be a boolean');
}
// ... a lot of validations
return x;
};
Why not:
const guarded = guard({
foo: string.required,
bar: boolean.required,
// ... other rules
})(x);
Installation
yarn add power-guard
# npm
npm i power-guard
Play with Your Favorite Framework
// Express.js
const { id, name } = guard({ id: number.required, name: string.required })(req.body);
// Koa
const { id, name } = guard({ id: number.required, name: string.required })(ctx.request.body);
Examples
Primitive Validation
import { number, string, boolean } from 'power-guard';
number.required(10); // 10
number.strict.required('10'); // error: string is not allowed in strict mode
number.gt(10).required('10'); // error: value should be greater than 10
string.with(10).required('a really long string'); // error: string has max length 10
string.optional(undefined); // undefined
boolean.required.array([true, false]); // [true, false]
boolean.required.array(true); // error: value should be an array
boolean.loose.required('1'); // true
Object Validation
import guard, { number, string, boolean } from 'power-guard';
const guardObject = guard({
foo: number.required,
bar: string.optional,
baz: boolean.required,
});
const { foo, bar, baz } = guardObject({ foo: 10, baz: true }); // 10, undefined, true
const { foo, bar, baz } = guardObject({ foo: '10', baz: 'true' }); // 10, undefined, true
const { foo, bar, baz } = guardObject({ foo: 'abc', baz: 'true' }); // error: foo should be a number
Validating Complex Objects
import guard, { number, string, enumGuard, boolean, array } from 'power-guard';
enum Foo {
key1 = 'key1',
key2 = 2,
}
const guardObject = guard({
foo: number.gte(10).required,
bar: string.escaped.optional,
baz: enumGuard(Foo).optional.array,
bla: boolean.loose.optional,
bnd: array(guard({ foo: number.strict.required })),
});
const guarded = guardObject({
foo: '10',
bar: '!abc ABC!',
baz: [2, 'key1'],
bla: 1,
bnd: [{ foo: 123 }, { foo: 456 }],
});
/*
{
foo: 10,
bar: 'abc ABC',
baz: [2, 'key1'],
bla: true,
bnd: [{ foo: 123 }, { foo: 456 }],
}
*/
Docs
The guard
function
This function receives a DataConfig
object as parameter, and returns a guard function which accepts an unknown variable and returns a typed object based on the config if validation succeeded, otherwise it will throw a PowerGuardError
.
In short, this is the function for validating objects. Say we want an object to have the shape like:
{ foo: 123, bar: 'abc' }
Then we just need to tell power-guard
:
const guardObject = guard({
foo: number.required,
bar: string.required,
});
You may notice there are various built-in guards, meanwhile deep customizations are also allowed:
const validate = (x: unknown): SomeType => {
// your code goes here
};
const guardObject = guard({
foo: validate,
});
const { foo } = guardObject(x); // foo will be `SomeType` if succeeded
Built-in Guards
Each of the guards below should follow with a .required
or .optional
, while the latter will allow undefined
. Simply append .array
in the end when needed e.g. boolean.required.array
.
boolean
| Boolean Guard | Description |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| boolean | A boolean value must be presented. 'true'
and 'false'
are acceptable. |
| boolean.strict | A boolean value must be presented. No implicit conversions allowed. |
| boolean.loose | A boolean value must be presented. 'true'
and 'false'
are acceptable. If the value can be converted to a number, then return false
iff the number equals 0, otherwise true
. |
enum
| Enum Guard | Description | | ------------------- | -------------------------------------- | | enumGuard(EnumType) | A value of EnumType must be presented. |
number
| Number Guard | Description | | ------------------------------- | ------------------------------------------------------------------------- | | number | A number value must be presented. Valid number string is also acceptable. | | number.strict | A number value must be presented. No implicit conversions allowed. | | number.with([lower, upper]) | A number value within [lower, upper] must be presented. | | number.with(range: NumberRange) | A number value within the range must be presented. | | number.gt(x) | A number value within (x, +∞) must be presented. | | number.gte(x) | A number value within [x, +∞) must be presented. | | number.lt(x) | A number value within (-∞, x) must be presented. | | number.lte(x) | A number value within (-∞, x] must be presented. |
type NumberRange = Partial<{
lower: number;
equalsLower: boolean;
upper: number;
equalsUpper: boolean;
}>;
Note you can chain these restrictions like: number.gt(10).lte(20).strict.required
string
| String Guard | Description | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | string | A string value must be presented. | | string.escaped | A string value must be presented. Punctuations and unknown chars will be removed and fullwidth chars will be converted into halfwidth. | | string.with(x) | A string value with a max length of x must be presented. |
Note you can chain them as well, e.g.: string.with(10).escaped.optional
array
There's a helper function for customized array guards with optional override:
function array(elemGuard: (x: unknown) => T): (x: unknown) => Array<T>;
function array(elemGuard: (x: unknown) => T, optional: true): (x: unknown) => Array<T> | undefined;
Traditional Type Guards
power-guard
will return a variable with the type you want instead of asserting types like traditional type guards. Browse types.ts if you would like to infer types.