Giter VIP home page Giter VIP logo

ow's Introduction



Coverage Status gzip size install size

Function argument validation for humans

For schema validation, I recommend zod.

Highlights

  • Expressive chainable API
  • Lots of built-in validations
  • Supports custom validations
  • Automatic label inference in Node.js
  • Written in TypeScript

Install

npm install ow

Usage

import ow from 'ow';

const unicorn = input => {
	ow(input, ow.string.minLength(5));

	// โ€ฆ
};

unicorn(3);
//=> ArgumentError: Expected `input` to be of type `string` but received type `number`

unicorn('yo');
//=> ArgumentError: Expected string `input` to have a minimum length of `5`, got `yo`

We can also match the shape of an object.

import ow from 'ow';

const unicorn = {
	rainbow: '๐ŸŒˆ',
	stars: {
		value: '๐ŸŒŸ'
	}
};

ow(unicorn, ow.object.exactShape({
	rainbow: ow.string,
	stars: {
		value: ow.number
	}
}));
//=> ArgumentError: Expected property `stars.value` to be of type `number` but received type `string` in object `unicorn`

Note: If you intend on using ow for development purposes only, use import ow from 'ow/dev-only' instead of the usual import ow from 'ow', and run the bundler with NODE_ENV set to production (e.g. $ NODE_ENV="production" parcel build index.js). This will make ow automatically export a shim when running in production, which should result in a significantly lower bundle size.

API

Complete API documentation

Ow includes TypeScript type guards, so using it will narrow the type of previously-unknown values.

function (input: unknown) {
	input.slice(0, 3) // Error, Property 'slice' does not exist on type 'unknown'

	ow(input, ow.string)

	input.slice(0, 3) // OK
}

ow(value, predicate)

Test if value matches the provided predicate. Throws an ArgumentError if the test fails.

ow(value, label, predicate)

Test if value matches the provided predicate. Throws an ArgumentError with the specified label if the test fails.

The label is automatically inferred in Node.js but you can override it by passing in a value for label. The automatic label inference doesn't work in the browser.

ow.isValid(value, predicate)

Returns true if the value matches the predicate, otherwise returns false.

ow.create(predicate)

Create a reusable validator.

const checkPassword = ow.create(ow.string.minLength(6));

const password = 'foo';

checkPassword(password);
//=> ArgumentError: Expected string `password` to have a minimum length of `6`, got `foo`

ow.create(label, predicate)

Create a reusable validator with a specific label.

const checkPassword = ow.create('password', ow.string.minLength(6));

checkPassword('foo');
//=> ArgumentError: Expected string `password` to have a minimum length of `6`, got `foo`

ow.any(...predicate[])

Returns a predicate that verifies if the value matches at least one of the given predicates.

ow('foo', ow.any(ow.string.maxLength(3), ow.number));

ow.optional.{type}

Makes the predicate optional. An optional predicate means that it doesn't fail if the value is undefined.

ow(1, ow.optional.number);

ow(undefined, ow.optional.number);

ow.{type}

All the below types return a predicate. Every predicate has some extra operators that you can use to test the value even more fine-grained.

Predicate docs.

Primitives

  • undefined
  • null
  • string
  • number
  • boolean
  • symbol

Built-in types

  • array
  • function
  • buffer
  • object
  • regExp
  • date
  • error
  • promise
  • map
  • set
  • weakMap
  • weakSet

Typed arrays

  • int8Array
  • uint8Array
  • uint8ClampedArray
  • int16Array
  • uint16Array
  • int32Array
  • uint32Array
  • float32Array
  • float64Array

Structured data

  • arrayBuffer
  • dataView
  • sharedArrayBuffer

Miscellaneous

  • nan
  • nullOrUndefined
  • iterable
  • typedArray

Predicates

The following predicates are available on every type.

not

Inverts the following predicate.

ow(1, ow.number.not.infinite);

ow('', ow.string.not.empty);
//=> ArgumentError: Expected string to not be empty, got ``

is(fn)

Use a custom validation function. Return true if the value matches the validation, return false if it doesn't.

ow(1, ow.number.is(x => x < 10));

ow(1, ow.number.is(x => x > 10));
//=> ArgumentError: Expected `1` to pass custom validation function

Instead of returning false, you can also return a custom error message which results in a failure.

const greaterThan = (max: number, x: number) => {
	return x > max || `Expected \`${x}\` to be greater than \`${max}\``;
};

ow(5, ow.number.is(x => greaterThan(10, x)));
//=> ArgumentError: Expected `5` to be greater than `10`

validate(fn)

Use a custom validation object. The difference with is is that the function should return a validation object, which allows more flexibility.

ow(1, ow.number.validate(value => ({
	validator: value > 10,
	message: `Expected value to be greater than 10, got ${value}`
})));
//=> ArgumentError: (number) Expected value to be greater than 10, got 1

You can also pass in a function as message value which accepts the label as argument.

ow(1, 'input', ow.number.validate(value => ({
	validator: value > 10,
	message: label => `Expected ${label} to be greater than 10, got ${value}`
})));
//=> ArgumentError: Expected number `input` to be greater than 10, got 1

message(string | fn)

Provide a custom message:

ow('๐ŸŒˆ', 'unicorn', ow.string.equals('๐Ÿฆ„').message('Expected unicorn, got rainbow'));
//=> ArgumentError: Expected unicorn, got rainbow

You can also pass in a function which receives the value as the first parameter and the label as the second parameter and is expected to return the message.

ow('๐ŸŒˆ', ow.string.minLength(5).message((value, label) => `Expected ${label}, to have a minimum length of 5, got \`${value}\``));
//=> ArgumentError: Expected string, to be have a minimum length of 5, got `๐ŸŒˆ`

It's also possible to add a separate message per validation:

ow(
	'1234',
	ow.string
		.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``)
		.url.message('This is no url')
);
//=> ArgumentError: Expected string, to be have a minimum length of 5, got `1234`

ow(
	'12345',
	ow.string
		.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``)
		.url.message('This is no url')
);
//=> ArgumentError: This is no url

This can be useful for creating your own reusable validators which can be extracted to a separate npm package.

TypeScript

Requires TypeScript 4.7 or later.

Ow includes a type utility that lets you to extract a TypeScript type from the given predicate.

import ow, {Infer} from 'ow';

const userPredicate = ow.object.exactShape({
	name: ow.string
});

type User = Infer<typeof userPredicate>;

Maintainers

Former:

Related

ow's People

Contributors

alexdriaguine avatar apostrophii avatar bartcallant avatar brandon93s avatar cpiber avatar fnesveda avatar gentilefulvio avatar geraintwhite avatar goto-bus-stop avatar hafffe avatar hanshsieh avatar ilkerceng avatar itaisteinherz avatar jasonhk avatar jon-codes avatar jorisre avatar kevva avatar leaumar avatar ltetzlaff avatar lukechilds avatar mmkal avatar panva avatar reed665 avatar richienb avatar samverschueren avatar semigradsky avatar sindresorhus avatar theguardianwolf avatar transitive-bullshit avatar vladfrangu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ow's Issues

Enable the `esModuleInterop` tsconfig flag

"esModuleInterop": true

See: https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/

It would let us use normal ES2015 import instead of this workaround: ec9483a#diff-4a1b7500a09bd52ebb309d776b744539R1

I tried enabling it, but the Lodash types were not compatible:

node_modules/@types/lodash/array/first.d.ts:4:25 - error TS2339: Property 'head' does not exist on type '{ default: LoDashStatic; }'.

4         first: typeof _.head; // tslint:disable-line:no-unnecessary-qualifier
                          ~~~~


node_modules/@types/lodash/collection/each.d.ts:4:24 - error TS2339: Property 'forEach' does not exist on type '{ default: LoDashStatic; }'.

4         each: typeof _.forEach; // tslint:disable-line:no-unnecessary-qualifier
                         ~~~~~~~


node_modules/@types/lodash/collection/eachRight.d.ts:4:29 - error TS2339: Property 'forEachRight' does not exist on type '{ default: LoDashStatic; }'.

4         eachRight: typeof _.forEachRight; // tslint:disable-line:no-unnecessary-qualifier
                              ~~~~~~~~~~~~


node_modules/@types/lodash/util/iteratee.d.ts:38:30 - error TS2339: Property 'identity' does not exist on type '{ default: LoDashStatic; }'.

38         iteratee(): typeof _.identity; // tslint:disable-line:no-unnecessary-qualifier
                                ~~~~~~~~

We need to either wait or send a PR to DefinitelyTyped.

Package status

I'm planning to use ow in multiple modules, but the current version is below 1.0.

Should I expect big changes till v1.0?
What's the plan / open tasks keeping the project away from being considered as final?

(Great work btw :))

ArgumentError instead TypeError

Is there a reason for using a custom error named ArgumentError instead of the build-in TypeError?

: thrown when passing arguments of the wrong type

Async validator

Is it possible to have async validator? Ex. check if a value exists in DB.

Add browser tests

I'm using ow quite a lot in front-ends as well (mainly with ngx-ow), think it would be nice to have some basic tests which checks that it runs correctly in a browser environment as well.

Feel free to ignore this though, I know that you don't really care and that the primary focus is Node.js. I definitely don't want to add lot's of extra complexity to get this working.

Env variable to turn off validation?

I think very specific validators, while potentially useful, could end up costing a lot at runtime. What if there were a way to instantiate a copy of ow that can be ignored?

The API could look something like:

import _ow from 'ow'

const ow = _ow.ignoreIf(process.env.NODE_ENV === 'production')

function veryExpensiveValidator(obj) {
  // ...
}

// doesn't do anything when NODE_ENV is production
ow(someObj, obj => ow.is(obj))

Suggestion: Flip parameter order and adding currying

Sorry for the jargony subject line. Just a suggestion by changing the order of the parameters and currying you could remove the need for a separate create function...

const password = "password";
const checkPassword = ow(ow.string.minLength(12));
checkPassword(password);

// or all at once
ow.create(ow.string.minLength(12), password);

And ow.any becomes

ow.any(ow.string.maxLength(3), ow.number)('foo');

Perhaps even accepting curried ow() calls as arguments...

const isNumericPassword = ow.any(ow.number, checkPassword);
isNumericPassword('foo')

etc.

Make `ow` interface more strict

We could write the Ow interface like this

export interface Ow {
	<T>(value: T, predicate: Predicate<T>): void;
}

This would already provide feedback to the user of the library because it enforces the value to be the correct type based on the predicate.

ow(123, m.array.minLength(2));

This currently works, with the new definition, it will fail and an error is thrown

Argument of type 'ArrayPredicate' is not assignable to parameter of type 'Predicate<123>'.


The only reason preventing this currently is that it would enforce us to rewrite some tests.

t.throws(() => m('12', m.array), 'Expected argument to be of type `array` but received type `string`');

We will have to cast the input to an any type in order for the compiler to not complain.

t.throws(() => m('12', m.array), 'Expected argument to be of type `array` but received type `string`');

Maybe we could write a test macro or something, not sure yet what's the most elegant way of solving this.

Error: Cannot find module '@sindresorhus/is'

Hey! Thanks for putting this together. This seems like a really great library.

Unfortunately, I'm unable to use the version on npm currently. Installing it works as expected, but once I require("ow"), an error is raised.

It looks like the issue is a number of production dependencies are listed in package.json as dev deps. :/

Here are reproduction steps:

ow-test โˆด npm init -y
Wrote to /home/stephen/ow-test/package.json:

{
  "name": "ow-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}



ow-test โˆด ni --save ow
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN [email protected] No description
npm WARN [email protected] No repository field.

+ [email protected]
added 1 package in 1.9s

ow-test โˆด echo 'console.log(require("ow"))' > index.js

ow-test โˆด node index.js
module.js:540
    throw err;
    ^

Error: Cannot find module '@sindresorhus/is'
    at Function.Module._resolveFilename (module.js:538:15)
    at Function.Module._load (module.js:468:25)
    at Module.require (module.js:587:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/home/stephen/ow-test/node_modules/ow/dist/lib/predicates/predicate.js:6:30)
    at Module._compile (module.js:643:30)
    at Object.Module._extensions..js (module.js:654:10)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)

ow-test โˆด node -v
v8.9.4

ow-test โˆด npm -v
5.6.0

ow-test โˆด tree node_modules/
node_modules/
โ””โ”€โ”€ ow
    โ”œโ”€โ”€ dist
    โ”‚   โ”œโ”€โ”€ index.d.ts
    โ”‚   โ”œโ”€โ”€ index.js
    โ”‚   โ”œโ”€โ”€ index.js.map
    โ”‚   โ”œโ”€โ”€ lib
    โ”‚   โ”‚   โ”œโ”€โ”€ argument-error.d.ts
    โ”‚   โ”‚   โ”œโ”€โ”€ argument-error.js
    โ”‚   โ”‚   โ”œโ”€โ”€ argument-error.js.map
    โ”‚   โ”‚   โ”œโ”€โ”€ operators
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ not.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ not.js
    โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ not.js.map
    โ”‚   โ”‚   โ”œโ”€โ”€ predicates
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ any.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ any.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ any.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ array.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ array.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ array.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ base-predicate.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ base-predicate.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ base-predicate.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ boolean.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ boolean.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ boolean.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ date.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ date.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ date.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ error.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ error.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ error.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ map.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ map.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ map.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ number.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ number.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ number.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ object.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ object.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ object.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ predicate.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ predicate.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ predicate.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ set.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ set.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ set.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ string.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ string.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ string.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ weak-map.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ weak-map.js
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ weak-map.js.map
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ weak-set.d.ts
    โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ weak-set.js
    โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ weak-set.js.map
    โ”‚   โ”‚   โ””โ”€โ”€ utils
    โ”‚   โ”‚       โ”œโ”€โ”€ has-items.d.ts
    โ”‚   โ”‚       โ”œโ”€โ”€ has-items.js
    โ”‚   โ”‚       โ”œโ”€โ”€ has-items.js.map
    โ”‚   โ”‚       โ”œโ”€โ”€ of-type-deep.d.ts
    โ”‚   โ”‚       โ”œโ”€โ”€ of-type-deep.js
    โ”‚   โ”‚       โ”œโ”€โ”€ of-type-deep.js.map
    โ”‚   โ”‚       โ”œโ”€โ”€ of-type.d.ts
    โ”‚   โ”‚       โ”œโ”€โ”€ of-type.js
    โ”‚   โ”‚       โ””โ”€โ”€ of-type.js.map
    โ”‚   โ””โ”€โ”€ test
    โ”‚       โ”œโ”€โ”€ any.d.ts
    โ”‚       โ”œโ”€โ”€ any.js
    โ”‚       โ”œโ”€โ”€ any.js.map
    โ”‚       โ”œโ”€โ”€ array-buffer.d.ts
    โ”‚       โ”œโ”€โ”€ array-buffer.js
    โ”‚       โ”œโ”€โ”€ array-buffer.js.map
    โ”‚       โ”œโ”€โ”€ array.d.ts
    โ”‚       โ”œโ”€โ”€ array.js
    โ”‚       โ”œโ”€โ”€ array.js.map
    โ”‚       โ”œโ”€โ”€ boolean.d.ts
    โ”‚       โ”œโ”€โ”€ boolean.js
    โ”‚       โ”œโ”€โ”€ boolean.js.map
    โ”‚       โ”œโ”€โ”€ buffer.d.ts
    โ”‚       โ”œโ”€โ”€ buffer.js
    โ”‚       โ”œโ”€โ”€ buffer.js.map
    โ”‚       โ”œโ”€โ”€ data-view.d.ts
    โ”‚       โ”œโ”€โ”€ data-view.js
    โ”‚       โ”œโ”€โ”€ data-view.js.map
    โ”‚       โ”œโ”€โ”€ date.d.ts
    โ”‚       โ”œโ”€โ”€ date.js
    โ”‚       โ”œโ”€โ”€ date.js.map
    โ”‚       โ”œโ”€โ”€ error.d.ts
    โ”‚       โ”œโ”€โ”€ error.js
    โ”‚       โ”œโ”€โ”€ error.js.map
    โ”‚       โ”œโ”€โ”€ function.d.ts
    โ”‚       โ”œโ”€โ”€ function.js
    โ”‚       โ”œโ”€โ”€ function.js.map
    โ”‚       โ”œโ”€โ”€ iterable.d.ts
    โ”‚       โ”œโ”€โ”€ iterable.js
    โ”‚       โ”œโ”€โ”€ iterable.js.map
    โ”‚       โ”œโ”€โ”€ map.d.ts
    โ”‚       โ”œโ”€โ”€ map.js
    โ”‚       โ”œโ”€โ”€ map.js.map
    โ”‚       โ”œโ”€โ”€ nan.d.ts
    โ”‚       โ”œโ”€โ”€ nan.js
    โ”‚       โ”œโ”€โ”€ nan.js.map
    โ”‚       โ”œโ”€โ”€ null-or-undefined.d.ts
    โ”‚       โ”œโ”€โ”€ null-or-undefined.js
    โ”‚       โ”œโ”€โ”€ null-or-undefined.js.map
    โ”‚       โ”œโ”€โ”€ null.d.ts
    โ”‚       โ”œโ”€โ”€ null.js
    โ”‚       โ”œโ”€โ”€ null.js.map
    โ”‚       โ”œโ”€โ”€ number.d.ts
    โ”‚       โ”œโ”€โ”€ number.js
    โ”‚       โ”œโ”€โ”€ number.js.map
    โ”‚       โ”œโ”€โ”€ object.d.ts
    โ”‚       โ”œโ”€โ”€ object.js
    โ”‚       โ”œโ”€โ”€ object.js.map
    โ”‚       โ”œโ”€โ”€ promise.d.ts
    โ”‚       โ”œโ”€โ”€ promise.js
    โ”‚       โ”œโ”€โ”€ promise.js.map
    โ”‚       โ”œโ”€โ”€ regexp.d.ts
    โ”‚       โ”œโ”€โ”€ regexp.js
    โ”‚       โ”œโ”€โ”€ regexp.js.map
    โ”‚       โ”œโ”€โ”€ set.d.ts
    โ”‚       โ”œโ”€โ”€ set.js
    โ”‚       โ”œโ”€โ”€ set.js.map
    โ”‚       โ”œโ”€โ”€ string.d.ts
    โ”‚       โ”œโ”€โ”€ string.js
    โ”‚       โ”œโ”€โ”€ string.js.map
    โ”‚       โ”œโ”€โ”€ symbol.d.ts
    โ”‚       โ”œโ”€โ”€ symbol.js
    โ”‚       โ”œโ”€โ”€ symbol.js.map
    โ”‚       โ”œโ”€โ”€ test.d.ts
    โ”‚       โ”œโ”€โ”€ test.js
    โ”‚       โ”œโ”€โ”€ test.js.map
    โ”‚       โ”œโ”€โ”€ typed-array.d.ts
    โ”‚       โ”œโ”€โ”€ typed-array.js
    โ”‚       โ”œโ”€โ”€ typed-array.js.map
    โ”‚       โ”œโ”€โ”€ undefined.d.ts
    โ”‚       โ”œโ”€โ”€ undefined.js
    โ”‚       โ”œโ”€โ”€ undefined.js.map
    โ”‚       โ”œโ”€โ”€ weak-map.d.ts
    โ”‚       โ”œโ”€โ”€ weak-map.js
    โ”‚       โ”œโ”€โ”€ weak-map.js.map
    โ”‚       โ”œโ”€โ”€ weak-set.d.ts
    โ”‚       โ”œโ”€โ”€ weak-set.js
    โ”‚       โ””โ”€โ”€ weak-set.js.map
    โ”œโ”€โ”€ license
    โ”œโ”€โ”€ package.json
    โ””โ”€โ”€ readme.md

7 directories, 141 files

Bundle dependencies

Since this module is going to be used in a lot of my modules, I'd like it to stay dependency-free. We should compile everything into one bundle file.

@SamVerschueren Is there any way to make the TS compiler include dependencies when it compiles to a bundle?

Large bundle size

Issuehunt badges

Hey guys,

Currently, the ow bundle weighs in at 119.31 kb (65.69 kb minified), which imho seems pretty excessive considering it's functionality.

This isn't as much of an issue for Node.js usage, but for modules which want to support both Node.js and browser usage, I think it's a serious blocker to adoption.

I'm not that familiar with the TypeScript compiler, but I wanted to start the discussion to see if there is any low-hanging fruit we could cut out to make it more attractive to browser targets.

BTW, this concern originated in this comment by a popular JS frontend coder.

IssueHunt Summary

itaisteinherz itaisteinherz has been rewarded.

Sponsors (Total: $82.00)

Tips

Dist bundle is broken

We use the banner-webpack-plugin to add our custom exports to the end of the file. It used to work, but now I notice that it emits the following

//# sourceMappingURL=index.js.mapmodule.exports = ow.default;
module.exports.default = ow.default;
//# sourceMappingURL=index.js.map

So although we specify that it should strip the first source map url, it doesn't. I dived into the plugin and it looks like the plugin does the correct job because it does strip out that part. So no idea where I should look. I should really write something myself for this, ugh...

Ow's presence causes "Buffer is not defined" issue when webpacked

First of all, love the library. Really great stuff.

However when I use this library in a React app that's got some webpack going on, I get the following error:

Uncaught ReferenceError: Buffer is not defined

I've isolated the issue to ow by
a) removing my references to ow code in the particular file (runs fine)
b) putting those references back, then hacking the following right into the beginning of the dist/index.js file of the imported ow module:

global.Buffer = global.Buffer || require("buffer").Buffer;

as per
meteor/meteor#8645 (comment).

While I'm aware the polyfill is potentially expensive, might save other people who love your library the headache..hint hint hint...;)

Refactor collection predicates

Some parts of the collection predicates are duplicate like the key check for instance. Want to extract these into a utils directory.

I already experimented with an abstract CollectionPredicate but that doesn't really work. It's very hard to declare generic error messages and doc comments. I'd rather have some more duplicate code with decent error messages and good inline ts docs.

Will take this one after #43 and #44 are merged in.

Support for i18n

Would a PR for i18n support be inline with your vision of this project?

ow.object validator does not work with Class|undefined from TS

I've simplified this example to make it easier to repro (I know the as Test|undefined makes no sense in the example).

import ow from 'ow'

class Test {
  public foo: number
}

ow(new Test() as Test|undefined, ow.object)

This fails to compile with:

Argument of type 'ObjectPredicate' is not assignable to parameter of type 'Predicate<Test | undefined>'.
  Types of property 'not' are incompatible.
    Type 'ObjectPredicate' is not assignable to type 'Predicate<Test | undefined>

Interestingly, if you remove the member variable foo then it compiles without problem.

Tested with ts 2.8.3, ow 0.4.0, node.js 10.3.0, npm 6.1.0

ow.string.numeric doesn't support negative values

I wanted to check with ow if I could use parseInt() on a string value. So I did ow(value, ow.string.numeric). It failed with the input of -1 which is a perfect valid numeric value. Is this something we should support?

Add TSLint

I'm going through all the rules and will add a first draft of the TSLint configuration. We can always finetune in the future.

I think it would be nice if we could extract our config to a separate repo (like XO) but for TS projects. But let's first come up with decent rules :).

intellisense does not work as expected

When I try to use this module from VSCode in common js, intellisense does not work correctly because of default export(I think):

image

image

This is mostly a nice to have and would be good if it could be fixed.

Add `optional` predicate

Issuehunt badges

This is something that popped in my head while I was exploring the any and optional predicate. Currently for any we have the following syntax.

ow(5, ow.any(ow.string, ow.number));

The proposed syntax for optional is

ow(x, ow.optional.string);

I already spent quite some time trying to get the type detection work correctly, but I just couldn't get it working. An alternative in line with the any predicate could be ow(x, ow.optional(ow.string)), but that's much ow's there.

An alternative syntax for the previous examples could be

ow.any(5, ow.string, ow.number)
ow.optional(x, ow.string)

The type definition for optional is now very easy to do because x should be either the type of the predicate or undefined. Using a chained operator like ow.optional.string on the other hand is very hard to do (might be even impossible).

I also think it would be easier to add more main operators like not. For instance ow.not(5, ow.string). The only downside is that I think ow(5, ow.not.string) is more readable. Same goes for ow(x, ow.optional.string).

I just wanted to discuss this before we decide to release and make the project opensource. Because it's quite breaking. I would be fine with both approaches. There are benefits and downsides to both. There might be even an other way of dealing with this that I didn't think of so just go ahead and let me know what you guys think.

// @sindresorhus @kevva @vadimdemedes

samverschueren earned $80.00 by resolving this issue!

Add `number.integerOrInfinity` predicate

I have many cases where an API supports an integer or Infinity, like this:

function foo(concurrency = Infinity) {
	
}

I want to ensure concurrency is an integer, but I also need to support Infinity. I know I could use ow.any for this, but it feels very verbose for such a common use-case.

Mandatory parameters?

Could there be a ow.mandatory that throws by using it as default arg?

function getPage(identifier = ow.mandatory()) {
    ow(identifier, ow.string);
}
ArgumentError: Expected a mandatory argument but received `undefined` or missing

Reference: 16. Mandatory Parameter Shorthand

mandatory = () => {
  throw new Error('Missing parameter!');
}

foo = (bar = mandatory()) => {
  return bar;
}

ow.any doesn't support labels

As a user, I would've expected this to work:

ow(foo, ow.any(ow.string, ow.object, ow.number).label('foo'))

But instead, I have to do the following which is awkward & redundant:

ow(foo, ow.any(ow.string.label('foo'), ow.object.label('foo'), ow.number.label('foo')))

Any thoughts on supporting the former syntax?

Pass an argument name to use in an error message

Errors like the following would be much more useful if they contained which argument is that error for:

unicorn(3);
//=> ArgumentError: Expected argument to be of type `string` but received type `number`

@sindresorhus proposed this to avoid passing argument names explicitly, but in that case variable names will be incorrect when the code is minified/precompiled.

I don't like doing this too, as it makes API less beautiful. I guess ow('key', key, ow.string...) would be ok.

Fails to minify with create-react-app

I'm getting this with the latest create-react-app, I think it's possibly due to the /dist not being compiled down to ES5? Had to take ow out for now for convenience, which is a shame ๐Ÿ˜•

There's some explanation here: http://bit.ly/2tRViJ9

$ react-scripts build:

Creating an optimized production build...
Failed to compile.

Failed to minify the code from this file:

 	./node_modules/ow/dist/index.js:1:794

Read more here: http://bit.ly/2tRViJ9

Expose label from predicate properly

Currently, when setting the label() on a predicate, that label is then set on the context object. This is fine for how it currently works. But I have a use case where I need to be able to read the label of a predicate in a proper way without retrieving the context in an ugly way and stripping of the backticks. That's why I suggest to add a getLabel() method (we can't use get label() because it interferes with our label() method).

use case

I want to make a package that can perform custom form validation in Angular by utilising all the nifty checks that ow has. I call it form validation on steroids.

A custom form validator returns nothing (undefined or null) if the form (or the control it works on) is valid. And it returns an object with the name of the validation and a value. For instance, a custom form validator for checking the maximum length of an input would look like this.

export function maxLength(length: number) {
	return (control: FormControl) => {
		if (control.value.length < length) {
			return null;
		}

		return {
			maxLength: `Length of value exceeded, got \`${control.value.length}\`, expected ${length}.`
		}
	}
}

Using this with a form would look like

this.formBuilder.group({
	name: ['default value', maxLength(20)]
});

I want to do this with ow! I want to write a generic validator where I can pass in an ow predicate so that I don't need to write custom validators for this behaviour.

this.formBuilder.group({
	name: ['default value', owValidator(ow.string.maxLength(20))]
})

๐ŸŽ‰ ๐ŸŽ‰

But! The problem is that if the validation fails, it should return an object with the name of the validator which failed. (See the maxLength property of the custom validator above).

solution

I see two solutions. I either use the label of the predicate, which seems like the most ow-ish way of doing this.

this.formBuilder.group({
	name: ['default value', owValidator(ow.string.maxLength(20).label('maxLength'))]
})

Or I change the owValidator() function to accept the keyword like this

this.formBuilder.group({
	name: ['default value', owValidator('maxLength', ow.string.maxLength(20))]
})

Both methods are feasible I think. Just wanted your opinion as well. I think other tools could benefit from this as well. If you create a module which accepts an ow predicate, it's nice if you could use the label internally to do things.

If we are going to expose the label, how are we going to do this? Via a getLabel() method or something? Not sure...

Validator ideas

Want to open a thread to discuss and think about all the nice validators/predicates we want to implement. Feel free to provide feedback, add new things, remove things etc. It's just a draft about things we could add.

all

  • optional (Means undefined is allowed) (Note: I don't think we should allow null, to align with default parameters) (Tracked by #58)
  • not (Inverts the following predicates) (#12)
  • notType(type) (Inverts just the argument) (Example: ow.string.notType(ow.empty))
  • any (For multiple predicates, for example, when something can be both a string and array) (#56)
  • is(function) (Accepts a function that receives the value from ow and is expected to return a boolean) (#55)
  • create(predicate) (Create reusable validators) (#42)

Primitives

string

  • length(number) (#15)
  • minLength(number) (#15)
  • maxLength(number) (#15)
  • match(regex: RegExp) (Seems like matches would better align with other names like includes, startsWidth?) (#15)
  • startsWith(string) (#15)
  • endsWith(string) (#15)
  • includes(string) (#15)
  • empty (Only useful when used with .not) (#15)
  • nonEmpty (#15)
  • equal(string) (#15)
  • alphanumeric (#16)
  • numeric (#15)
  • date (vali-date) (#15)

number

  • inRange(start: number, end: number) (#10)
  • equal(x: number) (#10)
  • greaterThan(x: number) (#10)
  • greaterThanOrEqual(x: number) (#10)
  • lessThan(x: number) (#10)
  • lessThanOrEqual(x: number) (#10)
  • integer (#10)
  • finite (#10)
  • infinite (#10)
  • positive (#10)
  • negative (#10)

boolean

symbol (#28)

undefined (#28)

null (#28)

Built-in types

array

  • ofType(type, โ€ฆ) (Accepts any is.x type) (TODO: Would be useful if this could also support any ow predicate for each element, so you could do: ow.array.ofType(ow.string.minLength(5))) (#20)
  • length(number) (#20)
  • minLength(number) (#20)
  • maxLength(number) (#20)
  • startsWith(string) (#20)
  • endsWith(string) (#20)
  • includes(string, โ€ฆ) (#20)
  • includesAny(string, โ€ฆ) (#20)
  • empty (Only useful when used with .not) (#20)
  • nonEmpty (#20)
  • deepEqual(array) (#20)

Date

  • before(date) (#26)
  • after(date) (#26)

Error

  • evalError (#27)
  • rangeError (#27)
  • referenceError (#27)
  • syntaxError (#27)
  • typeError (#27)
  • uriError (#27)
  • name(string) (#27)
  • message(string) (#27)
  • messageIncludes(string) (#27)

Map

  • size(number) (#36)
  • minSize(number) (#36)
  • maxSize(number) (#36)
  • keysOfType(type, โ€ฆ) (#36)
  • valuesOfType(type, โ€ฆ) (#36)
  • valuesOfTypeDeep(type, โ€ฆ) (#53)
  • hasKeys(string, โ€ฆ) (#36)
  • hasValues(string, โ€ฆ) (#36)
  • hasAnyKeys(string, โ€ฆ) (#36)
  • hasAnyValues(string, โ€ฆ) (#36)
  • empty (Only useful when used with .not) (#36)
  • nonEmpty (#36)
  • deepEqual(map) (#36)

object

  • valuesOfType(type, โ€ฆ) (Accepts any is.x type) (#48)
  • plain (#48)
  • empty (Only useful when used with .not) (#48)
  • nonEmpty (#48)
  • deepEqual(object) (#48)
  • instanceOf(object) (#48)
  • hasKeys(property, โ€ฆ) (#48)
  • hasAnyKeys(property, โ€ฆ) (#48)

set

  • size(number) (#39)
  • minSize(number) (#39)
  • maxSize(number) (#39)
  • ofType(type, โ€ฆ) (#39)
  • has(string, โ€ฆ) (#39)
  • hasAny(string, โ€ฆ) (#39)
  • empty (Only useful when used with .not) (#39)
  • nonEmpty (#39)
  • deepEqual(set) (#39)

weakMap

  • keysOfType(type, โ€ฆ) Not able to iterate over the keys in a WeakMap.
  • hasKeys(string, โ€ฆ) (#43)
  • hasAnyKeys(string, โ€ฆ) (#43)

weakSet

  • ofType(type, โ€ฆ) Not able to iterate over the keys in a WeakSet.
  • has(string, โ€ฆ) (#44)
  • hasAny(string, โ€ฆ) (#44)

function (#30)

buffer (#30)

regExp (#30)

promise (#30)

typedArray (#30)

int8Array(value) (#30)

uint8Array(value) (#30)

uint8ClampedArray(value) (#30)

int16Array(value) (#30)

uint16Array(value) (#30)

int32Array(value) (#30)

uint32Array(value) (#30)

float32Array(value) (#30)

float64Array(value) (#30)

arrayBuffer(value) (#30)

sharedArrayBuffer(value)

Was added in Node.js 9 if I'm not mistaken, so can't add it.

dataView(value) (#38)

nan(value) (#38)

nullOrUndefined(value) (#38)

iterable(value) (#30)

TypeScript 3

It's out: https://blogs.msdn.microsoft.com/typescript/2018/07/30/announcing-typescript-3-0/

We can probably replace this mess

ow/source/index.ts

Lines 45 to 54 in db4af81

any<T1>(p1: Predicate<T1>): Predicate<T1>;
any<T1, T2>(p1: Predicate<T1>, p2: Predicate<T2>): Predicate<T1 | T2>;
any<T1, T2, T3>(p1: Predicate<T1>, p2: Predicate<T2>, p3: Predicate<T3>): Predicate<T1 | T2 | T3>;
any<T1, T2, T3, T4>(p1: Predicate<T1>, p2: Predicate<T2>, p3: Predicate<T3>, p4: Predicate<T4>): Predicate<T1 | T2 | T3 | T4>;
any<T1, T2, T3, T4, T5>(p1: Predicate<T1>, p2: Predicate<T2>, p3: Predicate<T3>, p4: Predicate<T4>, p5: Predicate<T5>): Predicate<T1 | T2 | T3 | T4 | T5>;
any<T1, T2, T3, T4, T5, T6>(p1: Predicate<T1>, p2: Predicate<T2>, p3: Predicate<T3>, p4: Predicate<T4>, p5: Predicate<T5>, p6: Predicate<T6>): Predicate<T1 | T2 | T3 | T4 | T5 | T6>;
any<T1, T2, T3, T4, T5, T6, T7>(p1: Predicate<T1>, p2: Predicate<T2>, p3: Predicate<T3>, p4: Predicate<T4>, p5: Predicate<T5>, p6: Predicate<T6>, p7: Predicate<T7>): Predicate<T1 | T2 | T3 | T4 | T5 | T6 | T7>;
any<T1, T2, T3, T4, T5, T6, T7, T8>(p1: Predicate<T1>, p2: Predicate<T2>, p3: Predicate<T3>, p4: Predicate<T4>, p5: Predicate<T5>, p6: Predicate<T6>, p7: Predicate<T7>, p8: Predicate<T8>): Predicate<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8>;
any<T1, T2, T3, T4, T5, T6, T7, T8, T9>(p1: Predicate<T1>, p2: Predicate<T2>, p3: Predicate<T3>, p4: Predicate<T4>, p5: Predicate<T5>, p6: Predicate<T6>, p7: Predicate<T7>, p8: Predicate<T8>, p9: Predicate<T9>): Predicate<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9>;
any<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(p1: Predicate<T1>, p2: Predicate<T2>, p3: Predicate<T3>, p4: Predicate<T4>, p5: Predicate<T5>, p6: Predicate<T6>, p7: Predicate<T7>, p8: Predicate<T8>, p9: Predicate<T9>, p10: Predicate<T10>): Predicate<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10>;
with generic spreading.

@SamVerschueren I tried removing the above code and the test still passes. Are the tests faulty or is that code moot?

Do you think there are any places we should use the unknown keyword?

CI build failure on Node.js v6

build faliure

Not sure what's going on, but it seems to be a dependency installation error with babel-parser.

Also, I'm pretty sure travis-ci wants everyone to migrate over to travis-ci.com instead of travis-ci.org. It's a seamless transition; just updating your links.

Logo and tagline

Suggestions welcome. I'll try to come up with something when I have some time.

Would be fun to invent a meaning for ow. Like it's an acronym for something fun.

Validate if string is part of an array `.oneOf(string[])`

I had a use case where I needed to check if the input was a string and was part of an array. So it might be nice if we had a .in(list: string[]) predicate (or another name) which could cover this.

Example

const input = 'foo';

ow(input, ow.string.in(['foo', 'bar']));

Run tests on the bundled code

Issuehunt badges

Currently, the tests are not ran on the bundle we generate. It would be nice if we did this to make sure the bundle we ship works and we didn't forget something in the webpack config for instance.


IssueHunt Summary

sindresorhus sindresorhus has been rewarded.

Backers (Total: $20.00)

Submitted pull Requests


Tips

Refactor `is` predicate

I was writing the docs in #59. I had to document the is predicate and gave the following example.

const greaterThan = (max: number, x: number) => {
	return x > max || `Expected \`${x}\` to be greater than \`${max}\``;
};

m(5, m.number.is(x => greaterThan(10, x)));
//=> ArgumentError: Expected `5` to be greater than `10`

This made me wonder, should we refactor the implementation of is regarding the custom error message. I'm not 100% sure about it but wanted to get some feedback. Instead of just returning a string, we could either do one of the following.

1. Throw an error

const greaterThan = (max: number, x: number) => {
	if (x <= max) {
		throw new TypeError(`Expected \`${x}\` to be greater than \`${max}\``);
	}

	return true;
};

2. Return an error

const greaterThan = (max: number, x: number) => {
	return x > max || new TypeError(`Expected \`${x}\` to be greater than \`${max}\``);
};

Throwing a TypeError right now, but it would probably accept any type of Error. The message is just copied over to ArgumentError.

I would be fine with keeping the current implementation though :).

Use Ow in conditional checks

I would like to be able to use Ow directly in conditionals:

if (ow(options.foo, ow.boolean)) {

}

The above doesn't work as Ow doesn't return a boolean when the validation passes.

We have ow.isValid(), but it only returns a boolean. I need it to return true when it passes and throw an error when it doesn't.


Alternatives considered: Having Ow accept a callback that is only executed when the condition passes. However, I would prefer using an if-statement if possible.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.