Champion: Ruben Bridgewater, Jordan Harband
Author: Ruben Bridgewater [email protected]
Stage: 1
This proposal extends Object.getOwnPropertySymbols with an options object that enables callers to filter the result list by enumerability.
const allSymbols = Object.getOwnPropertySymbols(obj); // Current behavior (all)
const allSymbolsTwo = Object.getOwnPropertySymbols(obj, { enumerable: 'all' }); // Current behavior (all)
const enumerableSymbols = Object.getOwnPropertySymbols(obj, { enumerable: true });
const nonEnumerableSymbols = Object.getOwnPropertySymbols(obj, { enumerable: false });Object.getOwnPropertySymbols is invaluable for libraries that rely on symbol-keyed metadata. Frequently, however, a library needs only the enumerable (or only the non‑enumerable) symbols:
- Serializers / logger utilities wish to include only enumerable data.
- Meta‑programming tools often hide internals behind non‑enumerable symbols and need to avoid exposing them.
Today, developers must fetch all symbols and then filter manually:
function enumerableSymbols(object) {
return Object.getOwnPropertySymbols(object)
.filter(symbol => Object.getOwnPropertyDescriptor(object, symbol)?.enumerable);
}This pattern is error‑prone, incurs extra per‑property descriptor lookups, and cannot be optimized by engines. The proposed option provides an ergonomic, reliable, and performant solution.
Object.getOwnPropertySymbols ( O [ , options ] )
O– the object whose own symbol properties are to be returned.options– optional object with a single property:enumerable:boolean | "all"(default"all")true→ return only enumerable own symbol propertiesfalse→ return only non‑enumerable own symbol properties"all"→ return all own symbol properties (current behavior) Ifoptionsisundefinedornull, behavior is equivalent to omitting the parameter ("all").
- Let obj be ?
ToObject(O). - Let keys be ?
obj.[[OwnPropertyKeys]](). - Let symbols be a new empty List.
- For each key of keys, do
- If key is a Symbol, then
- If filter passes for key, append key to symbols.
- If key is a Symbol, then
- Return CreateArrayFromList(symbols).
Filter Determination:
- Let mode be the
enumerableoption value if provided, else"all". - If mode is
"all", filter always passes. - Else retrieve desc ← ?
obj.[[GetOwnProperty]](key).- If mode is
true, filter passes iff desc.[[Enumerable]] is true. - If mode is
false, filter passes iff desc.[[Enumerable]] is false.
- If mode is
- Order of returned symbols must remain the same relative order as in
[[OwnPropertyKeys]]. - No new observable operations other than the necessary
[[GetOwnProperty]]look‑ups whenmode ≠ "all". - Accessor side effects remain as in current spec (a property descriptor retrieval can invoke user code).
const hidden = Symbol("hidden");
const visible = Symbol("visible");
const obj = {};
Object.defineProperty(obj, hidden, { enumerable: false, value: 1 });
Object.defineProperty(obj, visible, { enumerable: true, value: 2 });
Object.getOwnPropertySymbols(obj);
Reflect.ownKeys(obj);
// [hidden, visible]
Object.getOwnPropertySymbols(obj, { enumerable: true });
// [visible]
Object.getOwnPropertySymbols(obj, { enumerable: false });
// [hidden]const original = Object.getOwnPropertySymbols;
Object.getOwnPropertySymbols ??= function getOwnPropertySymbols(O, options) {
const mode = options && Object.hasOwn(options, "enumerable")
? options.enumerable
: "all";
const symbols = original(O);
if (mode === "all") { return symbols; }
return symbols.filter(symbol => {
const { enumerable } = Object.getOwnPropertyDescriptor(object, symbol);
return mode ? enumerable : !enumerable;
});
};- No breaking changes: existing two‑argument usages are non‑existent (today, the function has arity 1).
- Behavior when
options.enumerable === "all"exactly matches current semantics. - Engines may optimize the enumeration filter internally, avoiding the extra descriptor allocations present in userland polyfills.
This proposal does not expose new data; it merely refines selection of already‑accessible property keys. Side‑effects via accessors are unchanged from the status quo.
A canonical polyfill (above) demonstrates feasibility; prototypes in V8 & SpiderMonkey are straightforward, as both engines filter keys during [[OwnPropertyKeys]] enumeration.
Libraries such as lodash, Angular, React, Node.js, Next.js, TypeScript, and many more implement filtering for symbol enumerability today. Native support will simplify their code and yield performance gains.
- console.log() / util.inspect()
- assert.deepStrictEqual()
- Should
enumerable: "all"be permitted explicitly, or treated as default only? - Should we expose additional filters (e.g., configurable, writable) in the same options object? – Out of scope for MVP.
Object.propertyCount(). It is related in a way that it applies similar performance optimizations.