///
///
///
import {Class, Falsy, TypedArray, ObservableLike, Primitive} from './types';
const typedArrayTypeNames = [
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
'Int16Array',
'Uint16Array',
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array',
'BigInt64Array',
'BigUint64Array'
] as const;
type TypedArrayTypeName = typeof typedArrayTypeNames[number];
function isTypedArrayName(name: unknown): name is TypedArrayTypeName {
return typedArrayTypeNames.includes(name as TypedArrayTypeName);
}
const objectTypeNames = [
'Function',
'Generator',
'AsyncGenerator',
'GeneratorFunction',
'AsyncGeneratorFunction',
'AsyncFunction',
'Observable',
'Array',
'Buffer',
'Object',
'RegExp',
'Date',
'Error',
'Map',
'Set',
'WeakMap',
'WeakSet',
'ArrayBuffer',
'SharedArrayBuffer',
'DataView',
'Promise',
'URL',
'FormData',
'URLSearchParams',
'HTMLElement',
...typedArrayTypeNames
] as const;
type ObjectTypeName = typeof objectTypeNames[number];
function isObjectTypeName(name: unknown): name is ObjectTypeName {
return objectTypeNames.includes(name as ObjectTypeName);
}
const primitiveTypeNames = [
'null',
'undefined',
'string',
'number',
'bigint',
'boolean',
'symbol'
] as const;
type PrimitiveTypeName = typeof primitiveTypeNames[number];
function isPrimitiveTypeName(name: unknown): name is PrimitiveTypeName {
return primitiveTypeNames.includes(name as PrimitiveTypeName);
}
export type TypeName = ObjectTypeName | PrimitiveTypeName;
// eslint-disable-next-line @typescript-eslint/ban-types
function isOfType(type: PrimitiveTypeName | 'function') {
return (value: unknown): value is T => typeof value === type;
}
const {toString} = Object.prototype;
const getObjectType = (value: unknown): ObjectTypeName | undefined => {
const objectTypeName = toString.call(value).slice(8, -1);
if (/HTML\w+Element/.test(objectTypeName) && is.domElement(value)) {
return 'HTMLElement';
}
if (isObjectTypeName(objectTypeName)) {
return objectTypeName;
}
return undefined;
};
const isObjectOfType = (type: ObjectTypeName) => (value: unknown): value is T => getObjectType(value) === type;
function is(value: unknown): TypeName {
if (value === null) {
return 'null';
}
switch (typeof value) {
case 'undefined':
return 'undefined';
case 'string':
return 'string';
case 'number':
return 'number';
case 'boolean':
return 'boolean';
case 'function':
return 'Function';
case 'bigint':
return 'bigint';
case 'symbol':
return 'symbol';
default:
}
if (is.observable(value)) {
return 'Observable';
}
if (is.array(value)) {
return 'Array';
}
if (is.buffer(value)) {
return 'Buffer';
}
const tagType = getObjectType(value);
if (tagType) {
return tagType;
}
if (value instanceof String || value instanceof Boolean || value instanceof Number) {
throw new TypeError('Please don\'t use object wrappers for primitive types');
}
return 'Object';
}
is.undefined = isOfType('undefined');
is.string = isOfType('string');
const isNumberType = isOfType('number');
is.number = (value: unknown): value is number => isNumberType(value) && !is.nan(value);
is.bigint = isOfType('bigint');
// eslint-disable-next-line @typescript-eslint/ban-types
is.function_ = isOfType('function');
is.null_ = (value: unknown): value is null => value === null;
is.class_ = (value: unknown): value is Class => is.function_(value) && value.toString().startsWith('class ');
is.boolean = (value: unknown): value is boolean => value === true || value === false;
is.symbol = isOfType('symbol');
is.numericString = (value: unknown): value is string =>
is.string(value) && !is.emptyStringOrWhitespace(value) && !Number.isNaN(Number(value));
is.array = (value: unknown, assertion?: (value: T) => value is T): value is T[] => {
if (!Array.isArray(value)) {
return false;
}
if (!is.function_(assertion)) {
return true;
}
return value.every(assertion);
};
is.buffer = (value: unknown): value is Buffer => (value as any)?.constructor?.isBuffer?.(value) ?? false;
is.nullOrUndefined = (value: unknown): value is null | undefined => is.null_(value) || is.undefined(value);
is.object = (value: unknown): value is object => !is.null_(value) && (typeof value === 'object' || is.function_(value));
is.iterable = (value: unknown): value is Iterable => is.function_((value as Iterable)?.[Symbol.iterator]);
is.asyncIterable = (value: unknown): value is AsyncIterable => is.function_((value as AsyncIterable)?.[Symbol.asyncIterator]);
is.generator = (value: unknown): value is Generator => is.iterable(value) && is.function_((value as Generator)?.next) && is.function_((value as Generator)?.throw);
is.asyncGenerator = (value: unknown): value is AsyncGenerator => is.asyncIterable(value) && is.function_((value as AsyncGenerator).next) && is.function_((value as AsyncGenerator).throw);
is.nativePromise = (value: unknown): value is Promise =>
isObjectOfType>('Promise')(value);
const hasPromiseAPI = (value: unknown): value is Promise =>
is.function_((value as Promise)?.then) &&
is.function_((value as Promise)?.catch);
is.promise = (value: unknown): value is Promise => is.nativePromise(value) || hasPromiseAPI(value);
is.generatorFunction = isObjectOfType('GeneratorFunction');
is.asyncGeneratorFunction = (value: unknown): value is ((...args: any[]) => Promise) => getObjectType(value) === 'AsyncGeneratorFunction';
is.asyncFunction = (value: unknown): value is ((...args: any[]) => Promise) => getObjectType(value) === 'AsyncFunction';
// eslint-disable-next-line no-prototype-builtins, @typescript-eslint/ban-types
is.boundFunction = (value: unknown): value is Function => is.function_(value) && !value.hasOwnProperty('prototype');
is.regExp = isObjectOfType('RegExp');
is.date = isObjectOfType('Date');
is.error = isObjectOfType('Error');
is.map = (value: unknown): value is Map => isObjectOfType