specify!
implicitly relies on the provided object to be extensible so that protocol implementations can be provided. Here is a quick demonstration from a REPL.
cljs.user> (def obj #js {:current nil})
[#js {:current nil}]
cljs.user> (js/Object.preventExtensions obj)
{"current" nil}
cljs.user> (specify! obj
IDeref
(-deref [^js this] (.-current this)))
{"current" nil}
Note that reify
does not document this requirement either. This leads to confusing situations where non-extensible objects returned by JavaScript libraries (e.g. React) are unable to be extended and the user has little clue why.
Similarly, set!
silently fails when objects are not extensible.
cljs.user> (set! (.-foo obj) "bar")
"bar"
cljs.user> obj
{"current" nil}
goog.object/set
is able to detect this and throw an error.
cljs.user> (goog.object/set obj "foo" "bar")
Execution error (TypeError) at (<cljs repl>:1).
Cannot add property foo, object is not extensible
:repl/exception!