npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@produck/deep-freeze-enumerable

v1.1.0

Published

Recursively freeze enumerable properties of ECMAScript objects.

Readme

@produck/deep-freeze-enumerable

Recursively freeze enumerable properties of ECMAScript objects.

Installation

npm install @produck/deep-freeze-enumerable

Usage

deepFreeze(object)

Recursively freeze enumerable properties of an object.

import { deepFreeze } from '@produck/deep-freeze-enumerable';

const obj = { a: 1, b: { c: 2 } };

deepFreeze(obj);

API

deepFreeze<T>(object: T): DeepReadonly<T>

Recursively freezes the enumerable properties of the given object.

Parameters:

  • object - Any ECMAScript object to freeze

Returns:

  • The same object instance, typed as recursively readonly

Silent Handling Cases

deepFreeze is intentionally defensive and will not throw in several edge cases.

  1. Enumerable property access throws When reading value[property] during recursion, a getter (or proxy get trap) may throw. Behavior: that property is skipped and recursion continues with other properties.

  2. Property enumeration throws The for...in loop may fail (for example, a proxy ownKeys trap throws). Behavior: recursion is skipped for that object, then Object.freeze(value) is still attempted.

  3. Object.freeze itself throws In rare cases, Object.freeze(value) can throw (for example, proxy trap failures). Behavior: the original value is returned as-is, possibly not frozen.

  4. Non-object values and function-valued properties null, primitives, and functions are not recursively frozen by this implementation. Behavior: they are returned unchanged.

TypeScript Type Notes

Symbol-valued properties

deepFreeze uses a const type parameter (<const T>), so TypeScript infers the narrowest possible type for the argument. This means unique symbol values keep their identity through DeepReadonly<T> without being widened to symbol.

import { deepFreeze } from '@produck/deep-freeze-enumerable';

declare const TOKEN: unique symbol;

const frozen = deepFreeze({
	token: TOKEN, // unique symbol
});

// frozen.token -> typeof TOKEN  (unique symbol preserved)

You can then use the frozen symbol values as precise computed property keys:

const obj = {
	[frozen.token]: 'hello',
};

obj[frozen.token]; // string

This also works with deeply nested structures:

const GET = Symbol('get');
const SET = Symbol('set');
const EMIT = Symbol('emit');

const METHODS = deepFreeze({
	STORE: {
		GET,  // typeof GET  (unique symbol preserved)
		SET,  // typeof SET  (unique symbol preserved)
	},
	EVENT: {
		EMIT, // typeof EMIT (unique symbol preserved)
	},
});

// METHODS.STORE.GET -> typeof GET
// METHODS.EVENT.EMIT -> typeof EMIT

Symbol literal is not unique symbol

TypeScript only types a Symbol() call as unique symbol when it is assigned to a const variable declaration or a static readonly class property. In all other positions — including object literal property values — Symbol() is inferred as the broad symbol type. This is a TypeScript language limitation, not specific to deepFreeze. Object.freeze has the same behavior:

// ❌ Symbol() inside an object literal is inferred as `symbol`
const bad = deepFreeze({
	ID: Symbol('id'),   // symbol, not unique symbol
});

// Object.freeze has the same problem:
const bad2 = Object.freeze({
	ID: Symbol('id'),   // symbol, not unique symbol
});

To preserve unique symbol identity, declare each symbol as a separate const variable first, then reference it in the object:

// ✅ Workaround: declare symbols as const variables first
const ID = Symbol('id');
const NAME = Symbol('name');

const good = deepFreeze({
	ID,     // typeof ID   (unique symbol)
	NAME,   // typeof NAME (unique symbol)
});

License

MIT