typechecker
Version:
Utilities to get and check variable types (isString, isPlainObject, isRegExp, etc)
154 lines (153 loc) • 4.79 kB
JavaScript
/* eslint quote-props:0 */
// Prepare
const isClassRegex = /^class\s|^function\s+[A-Z]/;
const isConventionalClassRegex = /^function\s+[A-Z]/;
const isNativeClassRegex = /^class\s/;
// -----------------------------------
// Values
/** Get the object type string */
export function getObjectType(value) {
return Object.prototype.toString.call(value);
}
export function isObject(value) {
// null and undefined are objects, hence the extra check
return value != null && typeof value === 'object';
}
export function isPlainObject(value) {
/* eslint no-proto:0 */
// null and undefined are objects, hence the extra check
return value != null && value.__proto__ === Object.prototype;
}
export function isNativeClass(value) {
// NOTE TO DEVELOPER: If any of this changes, isClass must also be updated
return (typeof value === 'function' && isNativeClassRegex.test(value.toString()));
}
export function isConventionalClass(value) {
return (typeof value === 'function' &&
isConventionalClassRegex.test(value.toString()));
}
export function isClass(value) {
return typeof value === 'function' && isClassRegex.test(value.toString());
}
export function isError(value) {
return value instanceof Error;
}
export function isDate(value) {
return getObjectType(value) === '[object Date]';
}
export function isArguments(value) {
return getObjectType(value) === '[object Arguments]';
}
export function isSyncFunction(value) {
return getObjectType(value) === '[object Function]';
}
export function isAsyncFunction(value) {
return getObjectType(value) === '[object AsyncFunction]';
}
export function isFunction(value) {
return isSyncFunction(value) || isAsyncFunction(value);
}
export function isRegExp(value) {
return getObjectType(value) === '[object RegExp]';
}
export function isArray(value) {
return ((typeof Array.isArray === 'function' && Array.isArray(value)) ||
getObjectType(value) === '[object Array]');
}
export function isNumber(value) {
return typeof value === 'number' || getObjectType(value) === '[object Number]';
}
export function isString(value) {
return typeof value === 'string' || getObjectType(value) === '[object String]';
}
export function isBoolean(value) {
return (value === true ||
value === false ||
getObjectType(value) === '[object Boolean]');
}
export function isNull(value) {
return value === null;
}
export function isUndefined(value) {
return typeof value === 'undefined';
}
export function isNullish(value) {
return value == null;
}
export function isMap(value) {
return getObjectType(value) === '[object Map]';
}
export function isWeakMap(value) {
return getObjectType(value) === '[object WeakMap]';
}
export function isEmptyArray(value) {
if (!isArray(value))
throw new Error('value was not an array');
return value.length === 0;
}
export function isEmptyPlainObject(value) {
if (!isPlainObject(value))
throw new Error('value was not a plain object');
// We could use Object.keys, but this is more efficient
for (const key in value) {
if (value.hasOwnProperty(key)) {
return false;
}
}
return true;
}
export function isEmptyMap(value) {
if (!isMap(value))
throw new Error('value was not a map');
return value.size === 0;
}
export function isEmptyWeakMap(value) {
if (!isWeakMap(value))
throw new Error('value was not a weak map');
return Object.keys(value).length === 0;
}
export function isEmptyKeys(value) {
if (value == null)
return false;
return Object.keys(value).length === 0;
}
// -----------------------------------
// General
/**
* The default {@link TypeMap} for {@link getType}.
export * AsyncFunction and SyncFunction are missing, as they are more specific types that people can detect afterwards.
* @readonly
*/
export const typeMap = Object.freeze({
array: isArray,
boolean: isBoolean,
date: isDate,
error: isError,
class: isClass,
function: isFunction,
null: isNull,
number: isNumber,
regexp: isRegExp,
string: isString,
undefined: isUndefined,
map: isMap,
weakmap: isWeakMap,
object: isObject,
});
/**
* Cycle through the passed {@link TypeMap} testing the value, returning the first type that passes, otherwise `null`.
* @param value the value to test
* @param customTypeMap defaults to {@link typeMap}
*/
export function getType(value, customTypeMap = typeMap) {
// Cycle through our type map
for (const key in customTypeMap) {
if (customTypeMap.hasOwnProperty(key)) {
if (customTypeMap[key](value)) {
return key;
}
}
}
// No type was successful
return null;
}