s-types
v1.4.1
Published
Library-independent type checking similar to React Proptypes
Downloads
33
Readme
s-types
Library-independent type checking similar to React PropTypes
Installation
npm install --save s-types
Usage
import T from 's-types';
const fooType = T({
foo: T.string,
bar: [T.number, T.optional],
baz: T.arrayOf(T.string)
});
const someObject = {
foo: 'Hello',
baz: ['world', '!']
};
fooType(someObject); // passes test, nothing logged to console
const fail = {
foo: 'Hello',
bar: 10,
baz: [1, 2, 3] // !!!
};
// optional second argument to provide name for better error messages
fooType(fail, 'Bad Object');
/*
Logs the following to stderr:
Failed typecheck in Bad Object
Expected prop 'baz' of type [array of strings]
You provided 'baz' of type [array of numbers]
*/
Using an array of types in the schema allows for multiple types to be used. These are OR
ed together.
// Use an array to allow multiple types
const schema = T({
foo: [T.string, T.number],
bar: T.any
});
const someObject = {
foo: 'Hello',
bar: 5
};
const anotherObject = {
foo: 5,
bar: null
};
const badObject = {
foo: null,
bar: null
};
schema(someObject); // passes
schema(anotherObject); // passes
schema(badObject); // fails
The T.schema
and T.arrayOf
methods allow for nesting complex structures. For example:
const nestedRules = T({
foo: T.string,
bar: T.schema({
baz: T.number,
qux: T.arrayOf(T.arrayOf(T.number))
})
});
const someObject = {
foo: 'Hello',
bar: {
baz: 5,
qux: [[1, 2], [3, 4]]
}
};
nestedRules(someObject); // passes
The T.exact
and T.oneOf
methods allow to restrict to an exact value. For instance, a property that must contain the value "foo"
could be defined like this:
const rules = T({
x: T.exact('foo'),
y: [T.exact('bar'), T.optional]
})
const obj1 = { x: 'foo' };
const obj2 = { x: 'foo', y: 'bar' };
const obj3 = { x: 'bar' };
rules(obj1); // passes
rules(obj2); // passes
rules(obj3); // fails
T.oneOf([a, b, c])
is a shorthand for [T.exact(a), T.exact(b), T.exact(c)]
. It allows you to specify an array of allowed values.
const rules = T({
x: T.oneOf(['foo', 'bar'])
})
const obj1 = { x: 'foo' };
const obj2 = { x: 'bar' };
const obj3 = { x: 'baz' };
rules(obj1); // passes
rules(obj2); // passes
rules(obj3); // fails
Types provided
T.any
T.array
(aliasT.arr
)T.arrayOf(type)
Example:T.arrayOf(T.string)
T.boolean
(aliasT.bool
)T.date
T.exact
Example:T.exact('foo')
T.function
(aliasT.fn
)T.integer
(aliasT.int
)T.nil
(prop isnull
orundefined
)T.not(type)
Example:T.not(T.string)
T.number
(aliasT.num
)T.null
(aliasT.NULL
)T.object
(aliasT.obj
)T.oneOf
Example:T.oneOf(['foo', 'bar'])
T.optional
(aliasT.undefined
)T.schema
(aliasT.interface
) Example:T.schema({ foo: T.string, bar: T.number })
T.string
(aliasT.str
)
Custom types
If the provided types are not sufficient, you can provide a custom type checking function to s-types.
These functions take one argument (the value of the property to be type checked) and return a boolean to indicate whether it is valid or not.
The function must also be assigned a type
attribute in order to allow for helpful error messages.
For example, T.string
is defined as follows:
T.string = function(x) {
return typeof x === 'string';
};
T.str.type = 'string';
To create a positiveNumber
type, you could do the following:
function positiveNumber(x) {
return typeof x === 'number' && x > 0;
}
postiveNumber.type = 'positive number';
const schema = {
n: positiveNumber
};
const obj = {
n: 12
};
T(schema)(obj); // passes
Usage in production
This should only be used in development and test environments, so when in production there is a mode that turns type checking into a noop.
Just set T.disabled
to true
before running any type checking.
One way to do this would be the following:
import T from 's-types';
T.disabled = process.env.node_env === 'production';
const fooType = T({
foo: T.string
});
const fail = {
foo: 5
};
// in a development environment, this logs an error message
// when process.env.node_env === 'production', this logs nothing
fooType(fail, 'Bad Object');
Usage in test environments
There is an option to change the typecheckers to throw
rather than log to console.error
. This can be enabled in test environments to make typechecking testable without having to check for messages in stdout.
import T from 's-types';
T.throws = true;
const fooType = T({
foo: T.string
});
const fail = {
foo: 5
};
fooType(fail, 'Bad Object'); // throws a TypeError
Why two functions?
The T(a)(b, c?);
syntax allows typecheckers to be reused.
For example:
const UserType = T({
age: T.number,
email: T.string,
emailVerified: T.boolean,
name: T.string,
friends: T.arrayOf(T.string)
});
const user1 = {
age: 21,
email: [email protected],
emailVerified: false,
name: 'John Doe',
friends: ['user2']
};
const user2 = {
age: 24,
email: [email protected],
emailVerified: true,
name: 'Jane Doe',
friends: ['user1']
};
UserType(user1, 'John Doe user object'); // passes
UserType(user2, 'Jane Doe user object'); // passes
// The `UserType` typechecker can now be exported and used elsewhere
Things to note
In most cases, the return value happens to be null
when there are no errors or a string if a type mismatch occurred. For some structures, like T.schema
, this does not always hold true and should not be relied upon. The only reliable output is whatever is logged to stderr (or the TypeError that is thrown if T.throws
is enabled). In addition, when T.disabled
is set to true
, the return value will always be undefined
.