type-parser v0.1.4
type-parser
Types in JavaScript are not very easy to work with and sometimes are misleading. For example:
1- Checking if a variable is an array:
if (v && v.constructor === Array)
2- Variables can be null OR undefined
if (v === null || v === undefined)
3- Some conversions throw errors, requiring try-catches:
JSON.parse(v)
4- Type conversions and validation vary among data types, making the code hard to follow:
if (v !== null &&& v !== undefined) {
const myArray = (a.constructor === Array) ? a : [a];
myArray.forEach((entry) => {
console.log(myArray[v]);
});
}
This small library provides a simple way to check and enforce type safety, inspired by Mongoose schemas. It also allows the creation of custom types.
Important: In order to avoid unexpected behavior, when you convert something to a type in this library, you will always get there requested type. For example: if you try to convert a string into a function, there returned value will be an empty function. You can, however, specify different return values in the options during the conversion (for example: undefined). So you can be sure that when you convert a value to a type, you will get that type no matter what!
This is how the examples above would look like with this library:
T.Array.isTypeOf(value); // 1
T.Empty.isTypeOf(value); // 2
T.Object.from(value); // 3
// 4
if (!T.Empty.isTypeOf(v)) {
const myArray = T.Array.from(a, { lenient: 'true' });
myArray.forEach((entry) => {
console.log(myArray[v]);
});
}
You can also make your own types:
if (T.MyCustomType.isTypeOf(value1)) {
k = T.MyOtherType.from(value2);
}
Check the examples/index.js file for some examples on how to use this module.
Installing
npm install --save type-parser
Using
Importing the library:
const T = require('type-parser');
Checking/converting the type of a value
Make a function call to T and pass the value you want to test or convert, then, call the methods isTypeOf or from in order to test/convert to another type; as the parameter to these methods, pass the types you wish to test/convert to.
Look at the section Built-in types for a list of the types included in the library.
For example, to convert the string "5" to the number 5:
const newValue = T.Number.from("5"); // 5
To check if the number 5 is a string:
const isString = T.String.isTypeOf(5); // false
Some methods accept options. Pass them as the second parameter:
const newArray1 = T.Array.from(5); // []
const newArray2 = T.Array.from(5, { mode: 'lenient' }); // [5]
Custom methods can accept callback too, pass them as the second parameter. If you are already passing an option as the second parameter, pass the callback as the third parameter.
T.MyCustomType.from(5, (result) => { console.log(result); });
T.MyCustomType.from(5, { mode: 'lenient' }, (result) => { console.log(result); });
Making custom types
To make a new type, you have to provide a title, a method isTypeOf that describes how the type can be checked, and/or a method from that describes how to convert to this type.
// This type is called 'MyType'.
// To convert something to it, it will first convert the value into a string, and prepend 'TYPE_' to it
// To test if something is of this type, it will check if the string 'TYPE' is present in the beginning of the value
T.register({
title: 'MyType',
from: function (v) {
return 'TYPE_' + (v + '');
},
isTypeOf: function (v) { {
if (!T.String.isTypeOf(v)) { return false; }
return /^TYPE/.test(v);
}},
});
// We can now use this type:
T.MyType.from('abc'); // TYPE_abc
T.MyType.isTypeOf('TYPE_abc'); // true
The methods isTypeOf and from are optional, but you must specify at least one of them.
You can also receive options and callbacks, if your methods are asynchronous or require more customization:
The optional field requireCallback can be used in order to make sure we get a callback from the method call.
T.register({
title: 'MyType',
requireCallback: true,
from: function (v, options, cb) {
...
},
isTypeOf: function (v, options, cb) { {
...
}},
});
// This is how you can call these methods
T.MyType.from('abc', { my options }, () => {});
// If you don't need to pass any options, just pass the callback as the second method
T.MyType.isTypeOf('TYPE_abc', () => {});
Tips when making custom types
1- You can use the built-in types to validate/convert your types
2- Be prepared to receive ANY types
3- Don't use case sensitive attributes for options, and throw errors if someone uses invalid options:
const mode = ((options.mode || 'strict') + '').toLowerCase();
// Warning message for unknown modes
if (!['strict', 'lenient', 'falsetoundefined'].includes(mode)) {
throw {
type: 'warning',
message: `WARNING: Unknown mode "${mode}" used for toBoolean`,
method: 'toBoolean',
};
}
4- When in strict mode (the default mode) always return the same data type as promised (if toArray, return an Array always) in order to prevent unexpected behavior.
Adding custom types to JSDoc
In any part of your code, add a JSDoc comment similar to this:
/**
* @name T
* @property YourCustomTypeName
* ...
*/
If you want to specify their types:
/**
* @typedef {{
* title: String,
* requireCallback: Boolean|undefined,
* _isTypeParserType: Boolean,
* isTypeOf: Object|undefined,
* from: Object|undefined,
* }} TypeParser_Type
*/
/**
* @name T
* @property {TypeParser_Type} YourCustomTypeName
* ...
*/
Listing existing types
Use the method list to print all the registered types in the console. This method will also return an array with their data:
T.list(); // Prints all the types registered in the console
// Returns: [{ title: 'String', hasFrom: true, hasIsTypeOf: true } ...]
Built-in types
Some common types were already built-in the library:
Type | Has method .isTypeOf | Has method .from |
---|---|---|
Boolean | Yes | Yes |
String | Yes | Yes |
Date | Yes | Yes |
Function | Yes | Yes |
Array | Yes | Yes |
Number | Yes | Yes |
Object | Yes | Yes |
Timestamp | Yes | Yes |
Empty | Yes | No |
ValidDate | Yes | No |
A brief explanation of these types:
- Boolean - Represents the type Boolean
- String - Represents the type String
- Date - Represents the type Date (the object). Note that this can be an 'Invalid Date' (see ValidDate).
- Function - Represents a function
- Array - Represents the type Array, but NOT an object
- Number - Represents the type Number
Object - Represents the type Object (including Dates), but NOT an array
Timestamp - Represents a timestamp. If converted into a timestamp, the result will be a number (unless specified otherwise in the options). If checking if the value is a timestamp, it will try to convert into a number and check if it generates a ValidDate.
Empty - Represents null or undefined. Something cannot be converted into Empty. T(...).to(T.Empty) will cause an error. When you check if something is Empty, it will check if it is either null or undefined.
ValidDate - Represents the type Date (the object), but not a date that, when stringifed, generates 'Invalid Date'. Something cannot be converted into a ValidDate. T(...).to(T.ValidDate) will cause an error. Use Date instead. When you check if something is a ValidDate, it will check if it is a date, and if the stringified version is not 'Invalid Date'.
Options for built-in types
To use different options, send the object as parameter with the modifiers you want. For example, if a method has a "lenient mode", pass it like this:
{ mode: "lenient" }
If it has a forceParseJson modifier and you want to activate it as well:
{ mode: "lenient", forceParseJson: true }
The default mode is always strict (if not provided)
to Array
The outputs will very if the input cannot be converted:
- strict mode: returns an empty array
- lenient mode: returns the input wrapped in an array
- allowUndefined mode: returns undefined
Other modifiers:
- forceParseJson (Boolean) If true, will force a JSON.parse in the output even when it was not possible to conver the input to an array. For example, if used with lenient mode, in the string "{a:1}", the result would be { a : 1 }, and not "{a:1}".
to Boolean
- strict mode: will parse 'true', true, 1, and '1' into true, the rest will be false
- lenient mode: will use JS's native boolean parser (!! operator)
- falseToUndefined mode: similar to strict mode, but will output undefined instead of false
to Date
The outputs will very if the input cannot be converted:
- strict mode: returns a Date object, but invalid
- allowUndefined mode: returns undefined
to Function
The outputs will very if the input cannot be converted:
- strict mode: if not possible to convert to a function, returns a function that does nothing
- allowUndefined mode: if not possible to convert to a function, returns undefined
to Number
The outputs will very if the input cannot be converted:
- Strict mode: if not possible to convert into a number, it is NaN
- NaNto0 mode: similar to strict mode, but will replace NaN with 0
- NaNtoUndefined mode: similar to strict mode, but will replace NaN with undefined
to Object
The outputs will very if the input cannot be converted:
- Strict mode: if not possible to convert into an object, returns an empty object
- allowUndefined mode: if not possible to convert into an object, returns undefined
to String
The outputs will very if the input cannot be converted:
- Strict mode: if not possible to convert to a string, returns an empty string
- Lenient mode: will try using the default string parser (var.toString() and var + '')
- emptyAsUndefined mode: similar to strict mode, but will return undefined instead
to Timestamp
The outputs will very if the input cannot be converted:
- Strict mode: returns NaN
- NaNTo0 mode: returns undefined
- NaNToUndefined mode: returns undefined
More information about built-in types
For more information about how the built-in types work, check the test suites in test/suites.