@httpx/plain-object
v2.0.2
Published
Fast and lightweight utility functions to check if a value is a plain object.
Downloads
1,183
Maintainers
Readme
@httpx/plain-object
Fast and lightweight (~80B) functions to check or assert that a value is a plain object.
A plain object is a basic JavaScript object, such as {}
, { data: [] }
, new Object()
or Object.create(null)
.
See how it compares to other libraries.
Install
$ npm install @httpx/plain-object
$ yarn add @httpx/plain-object
$ pnpm add @httpx/plain-object
Features
- 👉 Provide isPlainObject and assertPlainObject functions.
- 🦄 Convenience PlainObject typescript typings.
- 🚀 Faster than most alternatives, see benchmarks.
- 📐 Lightweight (starts at ~80B)
- 🛡️ Tested on node 18-22, browser, cloudflare workers and runtime/edge.
- 🙏 Cross-realms tolerant (node:vm runInNewContext,...)
- 🗝️ Available in ESM and CJS formats.
Documentation
👉 Official website or GitHub Readme
Usage
isPlainObject
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('({})')); // ✅
// ✅👇 Static built-in classes are treated as plain objects
// check for `isStaticBuiltInClass` to exclude if needed
isPlainObject(Math); // ✅
isPlainObject(JSON); // ✅
isPlainObject(Atomics); // ✅
// ❌👇 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); // ❌,
assertPlainObject
import { assertPlainObject } from '@httpx/plain-object';
import type { PlainObject } from '@httpx/plain-object';
function fn(value: unknown) {
// 👇 Throws `new TypeError('Not a PlainObject')` 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);
}
isStaticBuiltInClass
info: Since v2.0.0
Since v2.0.0, isPlainObject
will accept static built-in classes
as plain objects (Math, JSON, Atomics). If you need to exclude them,
a new typeguard has been created isStaticBuiltInClass
.
import { isPlainObject, isStaticBuiltInClass } from '@httpx/plain-object';
const v = Math; // or Atomics or JSON
if (isPlainObject(v) && !isStaticBuiltInClass(v)) {
console.log('v is a plain object but not a static built-in class');
}
PlainObject type
Generic
ìsPlainObject
and assertPlainObject
accepts a generic to provide type
autocompletion. Be aware that no runtime check are done. If you're looking for
runtime validation, check zod, valibot or other alternatives.
import { isPlainObject } from '@httpx/plain-object';
import type { PlainObject } from '@httpx/plain-object';
type CustomType = {
id: number;
data?: {
test: string[];
attributes?: {
url?: string | null;
caption?: string | null;
alternativeText?: string | null;
} | null;
} | null;
};
const value = { id: 1 } as unknown;
if (isPlainObject<CustomType>(value)) {
// ✅ Value is a PlainObject with typescript autocompletion
// Note that there's no runtime checking of keys, so they are
// `unknown | undefined`. They will require unsing `?.` to access.
const url = value?.data?.attributes?.url; // autocompletion works
// ✅ url is `unknown | undefined`, so in order to use it, you'll need to
// manually check for the type.
if (typeof url === 'string') {
console.log(url.toUpperCase());
}
}
PlainObject
import { assertPlainObject } from '@httpx/plain-object';
import type { PlainObject } from '@httpx/plain-object';
function someFn(value: PlainObject) {
//
}
const value = { key: 'value' } as unknown;
assertPlainObject(value);
someFn(value)
Benchmarks
Performance is continuously monitored thanks to codspeed.io.
RUN v2.1.2 /home/sebastien/github/httpx/packages/plain-object
✓ bench/comparative.bench.ts (7) 5269ms
✓ Compare calling isPlainObject with 110x mixed types values (7) 5267ms
name hz min max mean p75 p99 p995 p999 rme samples
· @httpx/plain-object: `isPlainObject(v)` 948,266.97 0.0010 0.0700 0.0011 0.0011 0.0013 0.0017 0.0034 ±0.10% 474134 fastest
· (sindresorhus/)is-plain-obj: `isPlainObj(v)` 796,997.41 0.0011 0.0718 0.0013 0.0012 0.0026 0.0028 0.0053 ±0.14% 398499
· @sindresorhus/is: `is.plainObject(v)` 544,333.86 0.0017 0.0359 0.0018 0.0018 0.0032 0.0037 0.0083 ±0.11% 272167
· estoolkit: `isPlainObject(v)` 87,689.00 0.0103 1.0569 0.0114 0.0112 0.0189 0.0208 0.0350 ±0.47% 43845
· redux: `isPlainObject(v)` 257,141.86 0.0030 0.3135 0.0039 0.0038 0.0070 0.0094 0.0219 ±0.23% 128571
· (jonschlinkert/)is-plain-object: `isPlainObject(v)` 403,749.92 0.0018 0.3947 0.0025 0.0024 0.0057 0.0083 0.0185 ±0.38% 201875
· lodash-es: `_.isPlainObject(v)` 9,949.41 0.0685 0.6957 0.1005 0.1101 0.1964 0.2144 0.2790 ±0.95% 4975 slowest
BENCH Summary
@httpx/plain-object: `isPlainObject(v)` - bench/comparative.bench.ts > Compare calling isPlainObject with 110x mixed types values
1.19x faster than (sindresorhus/)is-plain-obj: `isPlainObj(v)`
1.74x faster than @sindresorhus/is: `is.plainObject(v)`
2.35x faster than (jonschlinkert/)is-plain-object: `isPlainObject(v)`
3.69x faster than redux: `isPlainObject(v)`
10.81x faster than estoolkit: `isPlainObject(v)`
95.31x faster than lodash-es: `_.isPlainObject(v)`
See benchmark file for details.
Bundle size
Bundle size is tracked by a size-limit configuration
| Scenario (esm) | Size (compressed) |
|-------------------------------------------------------------|------------------:|
| import { isPlainObject } from '@httpx/plain-object
| ~ 80B |
| import { assertPlainObject } from '@httpx/plain-object
| ~ 134B |
| Both isPlainObject and assertPlainObject
| ~ 142B |
| import { isStaticBuiltInClass } from '@httpx/plain-object
| ~ 37B |
For CJS usage (not recommended) track the size on bundlephobia.
Compatibility
| Level | CI | Description |
|------------|----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Node | ✅ | CI for 18.x, 20.x & 22.x. |
| Browser | ✅ | Tested with latest chrome (vitest/playwright) |
| Browsers | ✅ | > 96% on 07/2024. Mins to Chrome 96+, Firefox 90+, Edge 19+, iOS 12+, Safari 12+, Opera 77+ |
| Edge | ✅ | Ensured on CI with @vercel/edge-runtime. |
| Cloudflare | ✅ | Ensured with @cloudflare/vitest-pool-workers (see wrangler.toml |
| Typescript | ✅ | TS 5.0 + / are-the-type-wrong checks on CI. |
| ES2022 | ✅ | Dist files checked with es-check |
| Performance| ✅ | Monitored with codspeed.io |
For older browsers: most frontend frameworks can transpile the library (ie: nextjs...)
Comparison with other libraries
| Library | Compat | Perf | CJS+ESM |
|----------------------------------------------------------------|-------------|--------------|---------|
| is-plain-obj | Differences | 1.09x slower | No |
| es-toolkit | No | | Yes |
| (@redux)isPlainObject | ✅ 100% | 2.80x slower | Yes |
redux/isPlainObject
100% compatible see tests.
@sindresorhus/is-plain-obj
This library wouldn't be possible without @sindresorhus is-plain-obj. Notable differences:
- [x] SLightly faster (10%)
- [x] ESM and CJS formats.
- [x] Named export.
- [x] Smaller bundle size.
- [x] Provide a
PlainObject
type andassertPlainObject
function. - [x] Typescript convenience
PlainObject
type.
Since v2, it diverges from is-plain-obj
by
- [x] Static built-in classes are considered as plain objects (use isStaticBuiltInClass to exclude).
- [x]
[Symbol.iterator]
is considered as a valid property for plain objects. - [x]
[Symbol.toStringTag]
is considered as a valid property for plain objects.`
Contributors
Contributions are welcome. Have a look to the CONTRIBUTING document.
Sponsors
If my OSS work brightens your day, let's take it to new heights together! Sponsor, coffee, or star – any gesture of support fuels my passion to improve. Thanks for being awesome! 🙏❤️
Special thanks to
License
MIT © belgattitude and contributors.