Can you destructure a JavaScript Object in ClojureScript?
Asked Answered
S

3

12

I was wondering if there was a way to get ClojureScript destructuring to work with JavaScript objects, such as:

(let [{:keys [a b]} #js{:a 10, :b 20}]
  (print a)  ;=> I get nil, but I'd want to get 10
  (print b)) ;=> I get nil, bu I'd want to get 20

Thank You

Snowclad answered 9/4, 2018 at 5:17 Comment(0)
L
16

Associative destructuring is based on get, which can be mapped onto goog.object/get for JavaScript objects by extending them to ILookup:

(extend-type object
  ILookup
  (-lookup 
   ([o k] 
     (goog.object/get o (name k)))
   ([o k not-found] 
     (goog.object/get o (name k) not-found))))

Even though this results in destructuring working on JavaScript objects, it isn't advisable to extend object in this way. It would be preferable to decorate an object instance to achieve a similar effect.

Here is an example using reify:

(defn wrap [o]
  (reify
    ILookup
    (-lookup [_ k]
      (goog.object/get o (name k)))
    (-lookup [_ k not-found]
      (goog.object/get o (name k) not-found))))

With wrap as defined above:

(let [{:keys [a b]} (wrap #js{:a 10, :b 20})]
  (print a)
  (print b))

Functions in libraries that offer a capability like wrap above include:

Leola answered 16/4, 2018 at 1:7 Comment(1)
I'm now curious, what's the rationale for the advice against extending objects as such?Snowclad
F
3

I haven't found a direct way to destructure JS object. You can convert JavaScript object to a ClojureScript datastructure and then destructure:

(let [{:keys [a b]} (js->clj #js {:a 10 :b 20}
                             :keywordize-keys true)]
  (print a)
  (print b))

If you don't use :keywordize-keys option in js->clj you need to use strs instead of keys in the destructuring

(let [{:strs [a b]} (js->clj #js {:a 10 :b 20})]
  (print a)
  (print b))
Foreknow answered 9/4, 2018 at 6:40 Comment(0)
M
3

Matt Huebert's js-interop library provides JavaScript object destructuring out of the box with its version of let. All you have to do is add ^:js in front of the binding form:

(ns foo.bar
  (:require [applied-science.js-interop :as j]))

(j/let [^:js {:keys [a b]} #js{:a 10, :b 20}]
  [a b])
;; => [10 20]

This applies recursively (there's an escape hatch, ^:clj) and works in j/fn and j/defn as well. These functions act like normal Clojure if the ^:js is left out.

Metzler answered 5/5, 2020 at 14:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.