atomic-fns
Version:
Like Lodash, but for ESNext and with types. Stop shipping code built for browsers from 2015.
464 lines (462 loc) • 13.9 kB
JavaScript
/**
* This module includes global functions and constants.
*
* @module globals
*/
/** This is just a noop function. */
export function noop() { }
/** A function that always returns `true`. */
export const True = () => true;
/** A function that always returns `false`. */
export const False = () => false;
/**
* A custom error class that inherits from the Error object.
* @class CustomError
* @extends {Error}
*/
export class CustomError extends Error {
/**
* Returns a new CustomError with the specified message.
* @param {string} message
* @constructor
*/
constructor(message) {
super(message);
this.name = this.constructor.name;
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
}
else {
this.stack = new Error(message).stack;
}
}
}
/**
* Custom error to signal an invalid index access.
* @extends {CustomError}
*/
export class IndexError extends CustomError {
}
/**
* Raised when a system function returns an I/O failure such as "file not found"
* or "disk full".
* @extends {CustomError}
*/
export class IOError extends CustomError {
}
/**
* Custom error to signal an invalid key access.
* @extends {CustomError}
*/
export class KeyError extends CustomError {
}
/**
* Custom error to signal an invalid operation.
* @extends {CustomError}
*/
export class NotImplementedError extends CustomError {
}
/**
* Custom error to signal an invalid value.
* @extends {CustomError}
*/
export class ValueError extends CustomError {
}
/**
* Raised when the second argument of a division or modulo operation is zero.
* @extends {CustomError}
*/
export class ZeroDivisionError extends CustomError {
}
/**
* Returns the true type of any value with correct detection for `null`, `Array`,
* `Object`, `Promise`, `Symbol`, and `NaN`. The correct values are inferred from {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag | Symbol.toStringTag} and the value's prototype.
* @param value
* @returns The value type.
*/
export function type(value) {
const result = typeof value;
if (result === 'object') {
if (value) {
if (value instanceof String)
return 'string';
if (value instanceof Boolean)
return 'boolean';
if (value instanceof Function)
return 'function';
if (value instanceof Array)
return 'array';
if (value instanceof Error)
return 'error';
if (value[Symbol.toStringTag])
return value[Symbol.toStringTag];
const className = str(value);
if (className === '[object Window]')
return 'object';
if (className.endsWith('Array]') || Array.isArray(value)) {
return 'array';
}
if (className.endsWith('Function]') || typeof value.call === 'function') {
return 'function';
}
// Other primitive types
if (className === '[object Promise]' || value instanceof Promise) {
return 'Promise';
}
}
else {
return 'null';
}
}
else if (result === 'function' && typeof value.call === 'undefined') {
return 'object';
}
else if (Number.isNaN(value))
return 'NaN';
return result;
}
/**
* Returns a string representing the object.
* @param obj
* @returns `obj.toString()` or `''` if `obj` is `null` or `undefined`.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString | Object.toString }
*/
export function str(obj) {
return obj?.toString() ?? '';
}
/** Check if value is a boolean type. */
export function isBool(x) {
return type(x) === 'boolean';
}
/** Check if value is an `Error` type. */
export function isError(x) {
return x instanceof Error;
}
/** Check if value is an iterable type. */
export function isIterable(x) {
if (x == null)
return false;
return typeof x[Symbol.iterator] === 'function';
}
/** Check if value is a native `Date` type. */
export function isDate(x) {
return x instanceof Date;
}
/** Check if value is an object type. */
export function isObject(x) {
return type(x) === 'object';
}
/** Check if value is a string type. */
export function isString(x) {
return type(x) === 'string';
}
/** Check if value is an Array type. */
export function isArray(x) {
return Array.isArray(x);
}
/**
* Check if value is Array-like type.
* A value is considered array-like if it's not a function and has a `.length` number property.
*/
export function isArrayLike(x) {
const T = type(x);
return T === 'array' || (T === 'object' && isNumber(x.length));
}
/** Check if value is a number type. */
export function isNumber(x) {
return type(x) === 'number';
}
/** Check if value is an integer number type. */
export function isInteger(x) {
return type(x) === 'number' && Math.trunc(x) === x;
}
/** Check if value is a bigint type. */
export function isBigint(x) {
return type(x) === 'bigint';
}
/** Check if value is a Promise type. */
export function isPromise(x) {
return type(x) === 'Promise';
}
/** Check if value is a function type. */
export function isFunction(x) {
return x?.constructor === Function;
}
/** Check if value is an async function type. */
export function isAsyncFunction(x) {
return x?.constructor.name === 'AsyncFunction';
}
/** Check if value is a generator function type. */
export function isGenerator(x) {
return x?.constructor.constructor?.name === 'GeneratorFunction';
}
/** Check if value is `null` or `undefined`. */
export function isNull(x) {
return x == null;
}
/** Check if value === `undefined`. */
export function isUndefined(x) {
return x === undefined;
}
/** Check if value is not `null` or `undefined`. */
export function notNull(x) {
return x != null;
}
/** Returns `true` for objects without length or falsy values. */
export function isEmpty(x) {
return len(x) === 0 ? true : !x;
}
/** Check if value is a `RegExp` type */
export function isRegExp(x) {
return x instanceof RegExp;
}
/** Check if value is a `Symbol` type */
export function isSymbol(x) {
return type(x) === 'symbol';
}
/** Check if value is a `Set` type. */
export function isSet(x) {
return x instanceof Set;
}
/** Check if value is a `Map` type. */
export function isMap(x) {
return x instanceof Map;
}
/** Check if value is a `WeakSet` type. */
export function isWeakSet(x) {
return x instanceof WeakSet;
}
/** Check if value is a `WeakMap` type. */
export function isWeakMap(x) {
return x instanceof WeakMap;
}
/**
* Returns the number of elements in a collection type.
*
* If `value.length` or `value.size()` is defined, this will be returned.
* If `value` is an `Object`, it returns the number of keys.
* @param value
* @returns The number of elements in the collection or `undefined`.
*/
export function len(value) {
if (value == null)
return;
if (isNumber(value.length)) {
return value.length;
}
if (isNumber(value.size)) {
return value.size;
}
if (isFunction(value.size)) {
return value.size();
}
if (isObject(value)) {
return Object.keys(value).length;
}
}
/**
* Generates a unique ID using random numbers. If a prefix is given, the ID is appended to it and returned as a string.
* @param {string} [pre=''] The id prefix.
* @returns {number|string} Returns the numeric or string ID.
* @example
```js
uniqueId()
// 3445556877
uniqueId('user_')
// 'user_1033763188'
```
*/
export function uniqueId(pre = '') {
// @ts-expect-error
return (pre || 0) + ((Math.random() * 1e10) >>> 0);
}
/**
* Generates a monotonically increasing, pseudo-random based uuid-v4 string. These are sortable, url-friendly and are 100% unique if the IDs are generated more than 1 millisecond apart. If two IDs are generated at the same millisecond, the chance of collision is still pretty low (1 in 10^15).
* @returns Returns a GUID formatted string.
* @example
```js
uuid()
// '183f8ef3-b8f0-4000-81f5-89234c836d00'
```
*/
export function uuid() {
const u = Date.now().toString(16) + Math.random().toString(16) + '0'.repeat(16);
return [u.substr(0, 8), u.substr(8, 4), '4000-8' + u.substr(13, 3), u.substr(16, 12)].join('-');
}
/**
* Checks if `obj.key` is a function, and calls it with any given `args`.
* @param {*} obj
* @param {PropertyKey} key
* @param {...any[]} args
* @return `obj.key(...args)` or `undefined`.
*/
export function call(obj, key, ...args) {
if (isFunction(get(key, obj))) {
return obj[key](...args);
}
}
/** Convert number to unicode character */
export function chr(x) {
return String.fromCodePoint(x);
}
/** Convert character to Unicode code point */
export function ord(x) {
return x.charCodeAt(0);
}
/**
* Returns an array of the own enumerable property names of `object`.
* @param object The given object.
* @returns The array of object keys.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys Object.keys()}
*/
export function keys(object) {
return Object.keys(object);
}
/**
* Returns an array of the own enumerable property values of `object`.
* @param object The given object.
* @returns The array of object keys.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values Object.values()}
*/
export function values(object) {
return Object.values(object);
}
/**
* Check if a given property is present in a given object.
* @param obj The object to check.
* @param attr A property key name.
* @returns `true` if the object has the property name.
*/
export function has(obj, attr) {
return obj && attr in obj;
}
export function get(key, obj, value) {
if (obj == null)
return value;
let paths = Array.isArray(key) ? key : [key];
if (typeof key === 'string') {
paths = key.split('.');
}
try {
let result = obj;
for (const k of paths) {
result = result[k];
}
return result === undefined ? value : result;
}
catch (error) {
return value;
}
}
/**
* Deletes a given property key from an object.
* @param attr The property name.
* @param x The given object.
* @returns The modified object.
*/
export function del(attr, x) {
if (x != null)
delete x[attr];
return x;
}
/**
* Adds a property to an object, or modifies the existing value.
* @param attr The name or Symbol of the property to be defined or modified.
* @param obj The container object.
* @param value The value associated with the property
* @param {boolean} [writable=false] If `true` the value may be changed later. Defaults to `false`.
* @param {boolean} [enumerable=false] If `true` this property shows up during enumeration of the properties in the object. Defaults to `false`.
* @returns
*/
export function set(attr, obj, value, writable = false, enumerable = false) {
return Object.defineProperty(obj, attr, {
value,
enumerable,
writable
});
}
export const HASH_KEY = Symbol.for('HASH_KEY');
export function hash(obj) {
if (typeof obj === 'string') {
return hashCode(obj);
}
if (typeof obj === 'object') {
if (get(HASH_KEY, obj) == null) {
return obj[HASH_KEY];
}
obj[HASH_KEY] = uniqueId();
return obj[HASH_KEY];
}
return obj;
}
/**
* A hash value generator for strings.
* @param {string} str The given string.
* @returns {number} The hash value of the string.
* @see {@link http://isthe.com/chongo/tech/comp/fnv/ FNV hash}
*/
export function hashCode(str) {
const FNV1_32A_INIT = 0x811c9dc5;
let hval = FNV1_32A_INIT;
for (let i = 0; i < str.length; ++i) {
hval ^= str.charCodeAt(i);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
return hval >>> 0;
}
/**
* Converts a number to a binary string.
* @param n The given number.
* @returns The binary string representation of `n`.
*/
export function bin(n) {
return n.toString(2);
}
/**
* Converts a number to a hexadecimal string.
* @param n The given number.
* @returns The hexadecimal string representation of `n`.
*/
export function hex(n) {
return n.toString(16);
}
/**
* Converts a number to an octal string.
* @param n The given number.
* @returns The octal string representation of `n`.
*/
export function oct(n) {
return n.toString(8);
}
/**
* Returns a floating point number constructed from a number or string `x`.
* @param x The number or string.
* @returns A floating point number parsed from the given value or `NaN`.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat parseFloat}
*/
export function float(x) {
return parseFloat(x);
}
/**
* Returns an integer number constructed from a number or string `x`.
* @param x The number or string.
* @returns An integer number parsed from the given value or `NaN`.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt parseInt}
*/
export function int(x, base = 10) {
return parseInt(x, base);
}
/**
* Converts the value to an array using `Array.from()`.
* @param value
* @returns A new array value.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from Array.from()}
*/
export function list(value) {
if (value != null)
return Array.from(value);
return [];
}
export function next(iter) {
return iter.next().value;
}