Skip to content

A zero-dependency utility for retrieving `Object.prototype.toString` tags. It is designed to handle hostile or exotic objects that cause native implementations to throw exceptions.

License

Notifications You must be signed in to change notification settings

scrrlt/safe-tag

Repository files navigation

safe-tag

npm version npm downloads CI codecov bundle size dependencies tree-shakable License: MIT TypeScript code style: prettier eslint security sponsor GitHub stars GitHub issues PRs welcome node

A zero-dependency utility for retrieving Object.prototype.toString tags. It is designed to handle hostile or exotic objects that cause native implementations to throw exceptions.

Technical overview

The native Object.prototype.toString.call() API can throw when encountering certain object states (for example revoked proxies or throwing Symbol.toStringTag getters). This library provides a wrapper that returns a string without propagating those exceptions.

Handled edge cases

  • Revoked Proxies: Avoids TypeError on revoked proxies.
  • Throwing Getters: Handles Symbol.toStringTag getters that throw.
  • Null/Undefined: Returns consistent strings for nullish values.
  • Masked Tags: By default, respects custom Symbol.toStringTag values; use unmaskTag to explicitly reveal the underlying tag when needed.

Installation

npm install @scrrlt/safe-tag

Usage

import safeTag, { unmaskTag } from "@scrrlt/safe-tag";

// Standard types
safeTag(123); // "[object Number]"
safeTag([]);  // "[object Array]"

// Hostile types
const { proxy, revoke } = Proxy.revocable({}, {});
revoke();
safeTag(proxy); // "[object Object]" (does not throw)

// Custom tags
const obj = { [Symbol.toStringTag]: "Custom" };
safeTag(obj);   // "[object Custom]" (read-only)
unmaskTag(obj); // "[object Object]" (temporarily mutates Symbol.toStringTag)

API

safeTag(value: unknown): string (default export)

  • Returns: A string in the form [object Type].
  • Exception handling: Guaranteed not to throw. For hostile objects (like revoked proxies), returns "[object Object]".
  • Behavior: Returns the tag as the engine sees it. Respects Symbol.toStringTag masks and never mutates the input.

unmaskTag(value: unknown): string

Advanced API that attempts to reveal the underlying tag by temporarily mutating the object's own Symbol.toStringTag descriptor.

  • Risk: May cause V8 to de-optimize the object (hidden class changes) due to descriptor mutation.
  • Side effects: Temporarily mutates Symbol.toStringTag on the object during the read, but is designed to restore the original descriptor and never throws. Falls back to safeTag(value) if unmasking fails.

Performance variant (safe-tag/fast)

For environments where inputs are trusted and performance is prioritized over resilience:

  • fastTag: Minimal wrapper around Object.prototype.toString.call(). Fastest, but may throw on revoked proxies or hostile objects.

Development and testing

  • Build: npm run build
  • Typecheck: npm run typecheck
  • Tests (dist): npm run test:all
  • Tests (src, faster dev loop): npm run test:src:all
  • Benchmarks: npm run bench

License

MIT