Installation
Quick Take
Idea
Often, npm packages export a function which takes an options object to configure it. As it is user-facing, it’s nice to validate it, especially if the configuration is complex.
This package saves you time validating options objects — just pass the defaults and it will infer the types and validate what user passed.
Features:
- Use a default options object to validate user-passed options
- Supplement or fully customise types (via a simple schema)
- Customise error messages so that errors show source as your library, even though
check-types-mini
threw them
For example, here’s a typical throw error generated by this library:
TypeError: yourLibrary/yourFunction(): [THROW_ID_01] opts.placeholder was customised to "false" which is not boolean but string
The point of check-types-mini
is to save your time: time spent coding up all these checks, time spent debugging, and even consumers’ time spent debugging your API when they try to use it wrongly. Every library that has options object will need some type checks if you let user tinker with it.
The only drawback is, this program will affect the performance — that’s why many apps don’t even validate the options’ values, especially boolean-ones.
API - checkTypesMini()
The main function checkTypesMini()
is imported like this:
It’s a function which takes three input arguments:
The main and only job of check-types-mini
is to throw errors when your library’s consumers are using it wrongly. Error messages can be customised:
Input argument | Type | Obligatory | Description |
---|---|---|---|
obj Type: Plain object Obligatory: yes | |||
obj | Plain object | yes | Options object after user’s customisation |
ref Type: Plain object Obligatory: no^ | |||
ref | Plain object | no^ | Default options — used to compare the types |
opts Type: Plain object Obligatory: no | |||
opts | Plain object | no | Optional options go here. |
The optional options object has the following shape:
Key | Type | Obligatory | Default | Description |
---|---|---|---|---|
ignoreKeys Type: Array or String Obligatory: no Default: [] (empty array) | ||||
ignoreKeys | Array or String | no | [] (empty array) | Instructs to skip all and any checks on keys, specified in this array. Put them as strings. |
ignorePaths Type: Array or String Obligatory: no Default: [] (empty array) | ||||
ignorePaths | Array or String | no | [] (empty array) | Instructs to skip all and any checks on keys which have given object-path notation-style path(s) within the obj . A similar thing to opts.ignoreKeys above, but unique (because simply key names can appear in multiple places whereas paths are unique). |
acceptArrays Type: Boolean Obligatory: no Default: false | ||||
acceptArrays | Boolean | no | false | If it’s set to true , value can be an array of elements, the same type as reference. |
acceptArraysIgnore Type: Array of strings or String Obligatory: no Default: [] (empty array) | ||||
acceptArraysIgnore | Array of strings or String | no | [] (empty array) | If you want to ignore acceptArrays on certain keys, pass them in an array here. |
enforceStrictKeyset Type: Boolean Obligatory: no Default: true | ||||
enforceStrictKeyset | Boolean | no | true | If it’s set to true , your object must not have any unique keys that reference object (and/or schema ) does not have. |
schema Type: Plain object Obligatory: no Default: {} | ||||
schema | Plain object | no | {} | You can set arrays of types for each key, overriding the reference object. This allows you more precision and enforcing multiple types. |
msg Type: String Obligatory: no Default: `` | ||||
msg | String | no | `` | A message to show. We like to include the name of the calling library, parent function and numeric throw ID. |
optsVarName Type: String Obligatory: no Default: opts | ||||
optsVarName | String | no | opts | How is your options variable called? It does not matter much, but it’s nicer to keep references consistent with your API documentation. |
Here are all defaults in one place for copying:
API — defaults
You can import defaults
:
It's a plain object:
The main function calculates the options to be used by merging the options you passed with these defaults.
API — version
You can import version
:
For example
The common pattern is,
- a) Define a defaults object. Later it will be used to validate user’s options, PLUS, if that’s not enough, you can allow users to provide arrays of the matching type (set
opts.acceptArrays
totrue
) - b) Alternatively, you can skip defaults object and provide a schema for each key via
opts.schema
. Just stick an object there, as a value, with all keys. Put allowed types in an array. Object.assign
cloned defaults onto the options object that comes from the input.- call
check-types-mini
with the above. - If input types mismatch, an error will be
throw
n.
import { checkTypesMini } from "check-types-mini";
function yourFunction(input, opts) {
// declare defaults, so we can enforce types later:
const defaults = {
placeholder: false,
};
// fill any settings with defaults if missing:
opts = Object.assign({}, defaults, opts);
// the check:
checkTypesMini(opts, defaults, {
msg: "newLibrary/yourFunction(): [THROW_ID_01]",
optsVarName: "opts",
});
// ...
}
let res = yourFunction(1, { placeholder: "zzz" });
// =>> [TypeError: 'newLibrary/yourFunction(): [THROW_ID_01] opts.placeholder was customised to "zzz" which is not boolean but string']
Sometimes you want to accept either value of certain type (like string
) or array of those (like an array of strings).
For example, if somebody sneaks in an array with strings and one null
, you want to throw
.
For these cases set opts.acceptArrays
to true
.
This will throw
an error:
import { checkTypesMini } from "check-types-mini";
checkTypesMini(
{
// < input
option1: "setting1",
option2: [true, true],
option3: false,
},
{
// < reference
option1: "setting1",
option2: false,
option3: false,
}
);
// => Throws, because reference's `option2` is Boolean ("false") but input `option2` is array ("[true, true]").
But when we allow arrays of the matching type, it won’t throw anymore:
import { checkTypesMini } from "check-types-mini";
checkTypesMini(
{
option1: "setting1",
option2: ["setting3", "setting4"],
option3: false,
},
{
option1: "setting1",
option2: "setting2",
option3: false,
},
{
acceptArrays: true,
}
);
// => Does not throw, because we allow arrays full of a matching type
If you want, you can blacklist certain keys of your objects so that opts.acceptArrays
will not apply to them. Just add keys into opts.acceptArraysIgnore
array.
opts.enforceStrictKeyset
When we were coding a new major version of ast-delete-object, we had to update all the unit tests too. Previously, the settings were set using only one argument, Boolean-type. We had to change it to be a plain object. We noticed that when we missed some tests, their Booleans were Object.assign
ed into a default settings object and no alarm was being raised! That’s not good.
Then we came up with the idea to enforce the keys of the object to match the reference and/or schema keys in options
. It’s on by default because we can’t imagine how you would end up with settings object that does not match your default settings object, key-wise, but if you don’t like that, feel free to turn it off. It’s opts.enforceStrictKeyset
Boolean flag.
opts.schema
Sometimes your API is more complex than a single type or array of them. Sometimes you want to allow, let’s say, string
or array
of strings or null
. What do you do?
Enter opts.schema
. You can define all the types for particular key, as an array:
import { checkTypesMini } from "check-types-mini";
checkTypesMini(
{
option1: "setting1",
option2: null,
},
{
option1: "zz",
option2: "yy", // << notice, it's given as string in defaults object
},
{
schema: {
option2: ["stRing", null],
},
}
);
// => does not throw
The types are case-insensitive and come from type-detect, a Chai library:
'object'
(meaning a plain object literal, nothing else)'array'
'string'
'null'
- and other usual types
Also, you can use more specific subtypes:
'true'
'false'
The 'true'
and 'false'
are handy in cases when API’s accept only one of them, for example, 'false'
and 'string'
, but doesn’t accept 'true'
.
For example,
const res = checkTypesMini(
{
// <--- this is object we're checking
option1: "setting1",
option2: true, // <--- bad
},
{
// <--- this is default reference object
option1: "zz",
option2: null,
},
{
// <--- opts
schema: {
option2: ["null", "false", "string"],
},
}
);
// => throws an error because `option2` should be either false or string, not true
All the type values you put into opts.schema
are not validated, on purpose, so please don’t make typos.
Regarding Typescript
Why would you use check-types-mini
in Typescript code, especially when you publish the program with type definitions?
For the record, TS static type checking won’t add any throw new Error()
statements to the JS code it compiles to. The TS might nag you, it might prevent you from compiling a program, but the shipped transpiled code won’t have any validation logic.
In most cases, if options object is simple, you can rely on usual Object.assign
or const resolvedOpts = {...defaults, ...opts}
.
When the options object is complex, for example, your program exports multiple methods which have obligatory options objects, check-types-mini
can help to reduce the validation part of your program.
For example, ast-monkey
has complex options and needs validation help. And it’s written in TypeScript and it ships with type definitions. check-types-mini
co-exists with TypeScript, streamlining the type checks.
For simple options objects, check-types-mini
is an overkill (unless you don’t care about your program’s performance, for example in small utility scripting programs).