UNPKG

@httpx/plain-object

Version:

Fast and lightweight utility functions to check if a value is a plain object.

109 lines (103 loc) 4.06 kB
type PlainObjectKey = string | number | symbol; type BasePlainObject = Record<PlainObjectKey, unknown>; interface DefaultBasePlainObject extends BasePlainObject { readonly __tag: 'default-plain-object'; } type Simplify<T> = { [P in keyof T]: T[P]; } & NonNullable<unknown>; type PlainObjectDeepPartialUnknown<T> = { [P in keyof T]?: NonNullable<T[P]> extends BasePlainObject ? Simplify<PlainObjectDeepPartialUnknown<NonNullable<T[P]>>> : unknown; }; type MsgOrErrorFactory = string | (() => Error); type PlainObject<TValue extends BasePlainObject = DefaultBasePlainObject> = TValue extends DefaultBasePlainObject ? Record<PlainObjectKey, unknown> : Simplify<PlainObjectDeepPartialUnknown<TValue>>; /** * Assert a value is a plain object * * @example * ```typescript * import { assertPlainObject } from '@httpx/plain-object'; * import type { PlainObject } from '@httpx/plain-object'; * * function fn(value: unknown) { * * // 👇 Throws `new TypeError('Not a plain object')` if not a plain object * assertPlainObject(value); * * // 👇 Throws `new TypeError('Custom message')` if not a plain object * assertPlainObject(value, 'Custom message'); * * // 👇 Throws custom error if not a plain object * assertPlainObject(value, () => { * throw new HttpBadRequest('Custom message'); * }); * * return value; * } * * try { * const value = fn({ key: 'value' }); * // ✅ Value is known to be PlainObject<unknown> * assertType<PlainObject>(value); * } catch (error) { * console.error(error); * } * ``` * * @throws TypeError */ declare function assertPlainObject<TValue extends BasePlainObject = DefaultBasePlainObject>(v: unknown, msgOrErrorFactory?: MsgOrErrorFactory): asserts v is TValue extends DefaultBasePlainObject ? BasePlainObject : PlainObject<TValue>; /** * Check if a value is a plain object * * A plain object is a basic JavaScript object, such as {}, { data: [] }, new Object() or Object.create(null). * * @example * ```typescript * import { isPlainObject } from '@httpx/plain-object'; * * // ✅👇 True * * isPlainObject({ }); // ✅ * isPlainObject({ key: 'value' }); // ✅ * isPlainObject({ key: new Date() }); // ✅ * isPlainObject(new Object()); // ✅ * isPlainObject(Object.create(null)); // ✅ * isPlainObject({ nested: { key: true} }); // ✅ * isPlainObject(new Proxy({}, {})); // ✅ * isPlainObject({ [Symbol('tag')]: 'A' }); // ✅ * * // ✅👇 (node context, workers, ...) * const runInNewContext = await import('node:vm').then( * (mod) => mod.runInNewContext * ); * isPlainObject(runInNewContext('({})')); // ✅ * * // ❌👇 False * * class Test { }; * isPlainObject(new Test()) // ❌ * isPlainObject(10); // ❌ * isPlainObject(null); // ❌ * isPlainObject('hello'); // ❌ * isPlainObject([]); // ❌ * isPlainObject(new Date()); // ❌ * isPlainObject(new Uint8Array([1])); // ❌ * isPlainObject(Buffer.from('ABC')); // ❌ * isPlainObject(Promise.resolve({})); // ❌ * isPlainObject(Object.create({})); // ❌ * isPlainObject(new (class Cls {})); // ❌ * isPlainObject(globalThis); // ❌ * * // ✅👇 Note that static built-in classes are treated as plain objects * // check for `isStaticBuiltInClass` to exclude if needed * * isPlainObject(Math); // ✅ * isPlainObject(JSON); // ✅ * isPlainObject(Atomics); // ✅ * ``` */ declare const isPlainObject: <TValue extends BasePlainObject = DefaultBasePlainObject>(v: unknown) => v is TValue extends DefaultBasePlainObject ? BasePlainObject : PlainObject<TValue>; type StaticBuiltInClass = Math | JSON | Atomics; declare const isStaticBuiltInClass: (v: unknown) => v is StaticBuiltInClass; export { type PlainObject, type StaticBuiltInClass, assertPlainObject, isPlainObject, isStaticBuiltInClass };