// oxlint-disable no-explicit-any import type { AnyAsyncFunction, AnyAsyncGeneratorFunction, AnyFunction, AnyGeneratorFunction, AnySyncFunction, NonPrimitive, PlainObject, Primitive, } from "#Source/type/index.ts" /** * @description 进行类型检测的常用手段包括: * * - Typeof: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof * https://tc39.es/ecma262/#sec-typeof-operator * - Instanceof: * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof * - ToString: * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString * * 检测可能不存在的变量: * * - Typeof value === "undefined" * - GlobalThis.value === undefined */ /** * @description 判断目标值是否为原始值(primitive)。 */ export const isPrimitive = (target: unknown): target is Primitive => { return ( isString(target) || isNumber(target) || isBoolean(target) || isBigInt(target) || isSymbol(target) || isNull(target) || isUndefined(target) ) } /** * @description 判断目标值是否为非原始值(non-primitive)。 */ export const isNonPrimitive = (target: unknown): target is NonPrimitive => { return isPrimitive(target) === false } /** * @description 判断目标值是否为字符串。 */ export const isString = (target: unknown): target is string => { return Object.prototype.toString.call(target) === "[object String]" } /** * @description 判断目标值是否为数字。 */ export const isNumber = (target: unknown): target is number => { return ( Object.prototype.toString.call(target) === "[object Number]" && isNaN(Number.parseFloat(String(target))) ) } /** * @description 判断目标值是否为 `NaN`。 */ export const isNaN = (target: unknown): boolean => { return Number.isNaN(target) } /** * @description 判断目标值是否为有限数字。 */ export const isFiniteNumber = (target: unknown): target is number => { return isNumber(target) && Number.isFinite(target) } /** * @description 判断目标值是否为无限数字。 */ export const isInfiniteNumber = (target: unknown): target is number => { return isNumber(target) && !Number.isFinite(target) } /** * @description 判断目标值是否为布尔值。 */ export const isBoolean = (target: unknown): target is boolean => { return Object.prototype.toString.call(target) === "[object Boolean]" } /** * @description 判断目标值是否为 `true`。 */ export const isTrue = (target: unknown): target is true => { return isBoolean(target) && target === true } /** * @description 判断目标值是否为 `false`。 */ export const isFalse = (target: unknown): target is false => { return isBoolean(target) && target === false } /** * @description 判断目标值是否为大整数(BigInt)。 */ export const isBigInt = (target: unknown): target is bigint => { return Object.prototype.toString.call(target) === "[object BigInt]" } /** * @description 判断目标值是否为符号(Symbol)。 */ export const isSymbol = (target: unknown): target is symbol => { return Object.prototype.toString.call(target) === "[object Symbol]" } /** * @description 判断目标值是否为 `null`。 */ export const isNull = (target: unknown): target is null => { return Object.prototype.toString.call(target) === "[object Null]" } /** * @description 判断目标值是否为 `undefined`。 */ export const isUndefined = (target: unknown): target is undefined => { return Object.prototype.toString.call(target) === "[object Undefined]" } /** * @description 判断目标值是否不是 `undefined`。 * * @see {@link https://stackoverflow.com/a/52097700} */ export const isDefined = (target: T | undefined): target is T => { return isUndefined(target) === false } /** * @description 判断目标值是否为 `null` 或 `undefined`。 */ export const isNil = (target: unknown): target is null | undefined => { return target === null || target === undefined } /** * @description 使用 `instanceof` 判断目标值是否为对象。 * * @see {@link isGeneralObject}, {@link isPlainObject} */ // oxlint-disable-next-line no-wrapper-object-types export const isObject = (target: unknown): target is Object => { return target instanceof Object } /** * @description 使用 `typeof` 判断目标值是否为广义对象。 * * A general object is an object that is: * - not `null`, not a `function`, not a `primitive`. * * Any other object is a general object: * - `Array` instance, `Map` instance, `WeakMap` instance, `Set` instance, `Date` instance, etc. * * @example * ``` * // Expect: true * const example1 = isGeneralObject([]) * // Expect: true * const example2 = isGeneralObject(new Map()) * // Expect: false * const example3 = isGeneralObject(null) * ``` * * @see {@link isObject}, {@link isPlainObject} */ // oxlint-disable-next-line no-wrapper-object-types export const isGeneralObject = (target: unknown): target is Object => { return target !== null && typeof target === "object" } /** * @description Predicate whether the target is a plain object. * * A plain object is an object that is: * - not `null`, not a `function`, not a `primitive` type, * - not an `Array`, not a `RegExp`, not a `Date`, not a `Map`, not a `Set`, not a `WeakMap`, * - not `window`, not a DOM `element`, not an `Error`, * - not any other customize object type. * * @see {@link isObject}, {@link isGeneralObject} */ export const isPlainObject = (target: unknown): target is PlainObject => { // 非 null、非 Function、非 Primitive if (target === null || typeof target !== "object") { return false } // 非 Array、非 Regexp、非 Date、非 Map、非 Set、非 Window、非 DOM、非 Error…… if (Object.prototype.toString.call(target) !== "[object Object]") { return false } // 非其它自定义类实例对象,allow only plain object literal or Object.create(null) const proto = Object.getPrototypeOf(target) return proto === Object.prototype || proto === null } /** * @description 判断目标值是否为空普通对象。 */ export const isEmptyPlainObject = (target: unknown): target is PlainObject => { return isPlainObject(target) && Reflect.ownKeys(target).length === 0 } /** * @description 判断目标值是否为数组。 */ export function isArray(target: T[]): target is T[] export function isArray(target: unknown): target is T[] export function isArray(target: unknown): target is T[] { return Array.isArray(target) } /** * @description 判断目标值是否为空数组。 */ export const isEmptyArray = (target: unknown): target is [] => { return isArray(target) && target.length === 0 } /** * @description 判断目标值是否为类数组对象(array-like)。 */ export function isArrayLike(target: ArrayLike): target is ArrayLike export function isArrayLike(target: unknown): target is ArrayLike export function isArrayLike(target: unknown): target is ArrayLike { return isGeneralObject(target) && "length" in target && isNumber(target.length) } /** * @description 判断目标值是否为 `Map`。 */ export function isMap(target: Map): target is Map export function isMap(target: unknown): target is Map export function isMap(target: unknown): target is Map { return Object.prototype.toString.call(target) === "[object Map]" } /** * @description 判断目标值是否为 `WeakMap`。 */ export function isWeakMap(target: WeakMap): target is WeakMap export function isWeakMap(target: unknown): target is WeakMap export function isWeakMap(target: unknown): target is WeakMap { return Object.prototype.toString.call(target) === "[object WeakMap]" } /** * @description 判断目标值是否为 `Set`。 */ export function isSet(target: Set): target is Set export function isSet(target: unknown): target is Set export function isSet(target: unknown): target is Set { return Object.prototype.toString.call(target) === "[object Set]" } /** * @description 判断目标值是否为 `WeakSet`。 */ export function isWeakSet(target: WeakSet): target is WeakSet export function isWeakSet(target: unknown): target is WeakSet export function isWeakSet(target: unknown): target is WeakSet { return Object.prototype.toString.call(target) === "[object WeakSet]" } /** * @description 判断目标值是否为 `Date`。 */ export const isDate = (target: unknown): target is Date => { return Object.prototype.toString.call(target) === "[object Date]" } /** * @description 判断目标值是否为 `RegExp`。 */ export const isRegExp = (target: unknown): target is RegExp => { return Object.prototype.toString.call(target) === "[object RegExp]" } /** * @description 判断目标值是否为 `Error`。 */ export const isError = (target: unknown): target is Error => { return Object.prototype.toString.call(target) === "[object Error]" } /** * @description 判断目标值是否为 `Promise`。 */ export function isPromise(target: Promise): target is Promise export function isPromise(target: unknown): target is Promise export function isPromise(target: unknown): target is Promise { return Object.prototype.toString.call(target) === "[object Promise]" } /** * @description 判断目标值是否为任意函数。 */ // oxlint-disable-next-line no-unsafe-function-type export const isFunction = (target: unknown): target is Function => { return typeof target === "function" } /** * @description 判断目标值是否为任意同步或异步函数类型。 */ export const isAnyFunction = (target: unknown): target is AnyFunction => { return isFunction(target) } /** * @description 判断目标值是否为同步函数。 */ export const isSyncFunction = (target: unknown): target is AnySyncFunction => { return Object.prototype.toString.call(target) === "[object Function]" } /** * @description 判断目标值是否为异步函数。 */ export const isAsyncFunction = (target: unknown): target is AnyAsyncFunction => { return Object.prototype.toString.call(target) === "[object AsyncFunction]" } /** * @description 判断目标值是否为生成器函数。 */ export const isGeneratorFunction = (target: unknown): target is AnyGeneratorFunction => { return Object.prototype.toString.call(target) === "[object GeneratorFunction]" } /** * @description 判断目标值是否为异步生成器函数。 */ export const isAsyncGeneratorFunction = (target: unknown): target is AnyAsyncGeneratorFunction => { return Object.prototype.toString.call(target) === "[object AsyncGeneratorFunction]" } /** * @description 判断目标值是否实现可迭代协议(iterable protocol)。 * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol} * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator} */ export function isIterable(target: Iterable): target is Iterable export function isIterable(target: unknown): target is Iterable export function isIterable(target: unknown): target is Iterable { // oxlint-disable-next-line no-unsafe-member-access return isGeneralObject(target) && typeof (target as any)[Symbol.iterator] === "function" } /** * @description 判断目标值是否实现异步可迭代协议。 * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator} */ export function isAsyncIterable(target: AsyncIterable): target is AsyncIterable export function isAsyncIterable(target: unknown): target is AsyncIterable export function isAsyncIterable(target: unknown): target is AsyncIterable { // oxlint-disable-next-line no-unsafe-member-access return isGeneralObject(target) && typeof (target as any)[Symbol.asyncIterator] === "function" } /** * @description 判断目标值是否实现迭代器协议(iterator protocol)。 * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol} */ export function isIterator( target: Iterator, ): target is Iterator export function isIterator( target: unknown, ): target is Iterator export function isIterator( target: unknown, ): target is Iterator { // oxlint-disable-next-line no-unsafe-member-access return isGeneralObject(target) && typeof (target as any).next === "function" } /** * @description 判断目标值是否实现异步迭代器协议。 */ export function isAsyncIterator( target: AsyncIterator, ): target is AsyncIterator export function isAsyncIterator( target: unknown, ): target is AsyncIterator export function isAsyncIterator( target: unknown, ): target is AsyncIterator { // oxlint-disable-next-line no-unsafe-member-access return isGeneralObject(target) && typeof (target as any).next === "function" } /** * @description 判断目标值是否同时满足可迭代对象与迭代器的条件。 */ export function isIterableIterator( target: IterableIterator, ): target is IterableIterator export function isIterableIterator( target: unknown, ): target is IterableIterator export function isIterableIterator( target: unknown, ): target is IterableIterator { return isIterator(target) && isIterable(target) } /** * @description 判断目标值是否同时满足异步可迭代对象与异步迭代器的条件。 */ export function isAsyncIterableIterator( target: AsyncIterableIterator, ): target is AsyncIterableIterator export function isAsyncIterableIterator( target: unknown, ): target is AsyncIterableIterator export function isAsyncIterableIterator( target: unknown, ): target is AsyncIterableIterator { return isAsyncIterator(target) && isAsyncIterable(target) }