2.0.9 β€’ Published 5 months ago

@httpx/plain-object v2.0.9

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

@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.

npm changelog codecov bundles node browserslist size downloads license

Install

$ npm install @httpx/plain-object
$ yarn add @httpx/plain-object
$ pnpm add @httpx/plain-object

Features

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.

CodSpeed Badge

 RUN  v3.0.4 /home/sebastien/github/httpx/packages/plain-object


 βœ“ bench/comparative.bench.ts > Compare calling isPlainObject with 110x mixed types values 5684ms
     name                                                       hz     min     max    mean     p75     p99    p995    p999     rme  samples
   Β· "@httpx/plain-object": `isPlainObject(v)`        1,832,016.65  0.0004  0.4702  0.0005  0.0005  0.0009  0.0011  0.0018  Β±0.49%   916009   fastest
   Β· "is-plain-obj":"4.1.0": 'isPlainObj(v)'          1,565,569.05  0.0005  0.5879  0.0006  0.0006  0.0014  0.0015  0.0018  Β±0.53%   782785
   Β· "@sindresorhus/is":"7.0.1": 'is.plainObject(v)'    944,124.29  0.0008  0.5417  0.0011  0.0010  0.0017  0.0023  0.0032  Β±0.38%   472063
   Β· "es-toolkit":"1.31.0": 'isPlainObject(v)'        1,432,993.92  0.0005  0.4105  0.0007  0.0007  0.0012  0.0016  0.0028  Β±0.56%   716497
   Β· "redux":"5.0.1": 'isPlainObject(v)'                651,167.40  0.0012  0.7868  0.0015  0.0015  0.0021  0.0028  0.0071  Β±0.55%   325584
   Β· "is-plain-object":"5.0.0": 'isPlainObject(v)'      828,601.23  0.0010  0.7233  0.0012  0.0012  0.0022  0.0028  0.0106  Β±0.68%   414301
   Β· lodash-es:"4.17.21": '_.isPlainObject(v)'           23,543.74  0.0332  0.6199  0.0425  0.0399  0.1037  0.1168  0.2867  Β±0.80%    11772   slowest
                                                                                                                                                    
 BENCH  Summary                                                                                                                                     
                                                                                                                                                    
  "@httpx/plain-object": `isPlainObject(v)` - bench/comparative.bench.ts > Compare calling isPlainObject with 110x mixed types values               
    1.17x faster than "is-plain-obj":"4.1.0": 'isPlainObj(v)'                                                                                       
    1.28x faster than "es-toolkit":"1.31.0": 'isPlainObject(v)'                                                                                     
    1.94x faster than "@sindresorhus/is":"7.0.1": 'is.plainObject(v)'                                                                               
    2.21x faster than "is-plain-object":"5.0.0": 'isPlainObject(v)'
    2.81x faster than "redux":"5.0.1": 'isPlainObject(v)'
    77.81x faster than lodash-es:"4.17.21": '_.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~ 133B
Both isPlainObject and assertPlainObject~ 141B
import { isStaticBuiltInClass } from '@httpx/plain-object~ 37B

For CJS usage (not recommended) track the size on bundlephobia.

Compatibility

LevelCIDescription
Nodeβœ…CI for 18.x, 20.x & 22.x.
Browserβœ…Tested with latest chrome (vitest/playwright)
Browserslistβœ…> 95% on 01/2025. defaults, chrome >= 96, firefox >= 105, edge >= 113, safari >= 15, ios >= 15, opera >= 103, not dead
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

LibraryCompatPerfCJS+ESM
is-plain-objDifferences1.17x slowerNo
es-toolkitNo1.25x slowerYes
(@redux)isPlainObjectβœ… 100%2.76x slowerYes
(lodash)isPlainObjectNo83.50x slowerNo

redux/isPlainObject

100% compatible see tests.

@sindresorhus/is-plain-obj

This library wouldn't be possible without @sindresorhus is-plain-obj. Notable differences:

  • Slightly faster (10%)
  • ESM and CJS formats.
  • Named export.
  • Smaller bundle size.
  • Provide a PlainObject type and assertPlainObject function.
  • Typescript convenience PlainObject type.

Since v2, it diverges from is-plain-obj by

  • Static built-in classes are considered as plain objects (use isStaticBuiltInClass to exclude).
  • [Symbol.iterator] is considered as a valid property for plain objects.
  • [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 Β© SΓ©bastien Vanvelthem and contributors.

2.0.3

11 months ago

2.0.5

9 months ago

2.0.4

10 months ago

2.0.7

5 months ago

2.0.6

9 months ago

2.0.9

5 months ago

2.0.8

5 months ago

2.0.2

1 year ago

2.0.1

1 year ago

2.0.0

1 year ago

1.2.0

1 year ago

1.1.3

1 year ago

1.1.2

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.0

1 year ago

0.1.0

1 year ago