Fork me on GitHub
#clojurescript
<
2022-04-13
>
rayat02:04:22

Out of curiosity, as it was brought up from the above thread on reagent props args, has anyone thought of, or is anyone knowledgeable enough to explain the feasibility of, utilizing typescript types (from provided declaration files, if present in libraries or provided by us) to do some linting/static checking (eg arity comes to mind), via kondo or something?

erre lin07:04:29

Speaking of checking arity using a linter for cljs, I think I need to add a bit extra info to my previous thread. My confusion happened when I read the example code by https://github.com/metosin/reitit/blob/3dff4c84aa748618cafc561e16ecdecf90ce87fb/examples/frontend/src/frontend/core.cljs#L56:

(defn about-page []
  [:div
   [:h2 "About frontend"]
   [:ul
    [:li [:a {:href ""} "external link"]]
    [:li [:a {:href (rfe/href ::foobar)} "Missing route"]]
    [:li [:a {:href (rfe/href ::item)} "Missing route params"]]]

;; other components ...

(defn current-page []
  [:div
   [:ul
    [:li [:a {:href (rfe/href ::frontpage)} "Frontpage"]]
    [:li [:a {:href (rfe/href ::about)} "About"]]
    [:li [:a {:href (rfe/href ::item {:id 1})} "Item 1"]]
    [:li [:a {:href (rfe/href ::item {:id 2} {:foo "bar"})} "Item 2"]]]
   (if @match
     (let [view (:view (:data @match))]
       [view @match])) ;; <=== this line
   [:pre (with-out-str (fedn/pprint @match))]])

(def routes
  [["/"
    {:name ::frontpage
     :view home-page}]

   ["/about"
    {:name ::about
     :view about-page}]

   ["/item/:id"
    {:name ::item
     :view item-page
     :parameters {:path {:id int?}
                  :query {(ds/opt :foo) keyword?}}}]])

(defn init! []
  (rfe/start!
    (rf/router routes {:data {:coercion rss/coercion}})
    (fn [m] (reset! match m))
    ;; set to false to enable HistoryAPI
    {:use-fragment true})
  (r/render [current-page] (.getElementById js/document "app")))
As I understand it, in the above code snippet, in function current-page , the line [view @match] will inevitably at some point becomes [about-page @match] , while about-page itself is a render function that takes no props at all. Since there is no arity checking in cljs, and as explained in the above thread, passing arguments to about-page is fine because they will be ignored. If a linter checks arity in cljs in such cases, will there be many false positives? :thinking_face: Which I think might upset those who have got used to the current cljs behavior. Anyway, I'm pretty new to cljs and know little about kondo or the linting of cljs in general. Glad to see if there will be more suggestions/ideas on this topic. I just think adding the above background will help a little. Thank you all.

1
sb08:04:42

I have a query `

(-> (js/fetch (str "api/get-something?q=" query-params))
    (.then (fn [resp] ...
Is that possible to drop parameters eg like at post: (js/fetch url {:method “POST :body params} ..) so in this case add as (js/fetch url {:params #js {:q query-params} is that possible to solve in this way?

sb09:04:00

Ok I see, the main js/fetch api doesn’t support this.

p-himik10:04:22

Yeah, you should use URLSearchParams with it in such cases.

👀 1
isak14:04:40

Anyone use a open source ClojureScript/JavaScript mobile list view component with swipe actions that they are happy with? Kind of like this:

isak14:04:51

The mail clients in iOS are a better example

lilactown15:04:21

web or react native?

pinkfrog15:04:25

For a JS function,

({pressed}) => { console.log(pressed)} 
How can I achieve the similar behavior of destructuring on the pressed field?

isak15:04:10

You'd have to wrap/convert it first, then use the normal Clojure map destructuring. https://github.com/mfikes/cljs-bean#cljs-bean May not be worth it.

👍 1
lilactown15:04:27

you can access the field directly instead of destructuring it, e.g.

(fn [^js ev]
  (let [pressed (.-pressed ev)]
    (js/console.log pressed)))

lilactown16:04:03

bean is good if you want to do a lot to map-like things with the value. if all you want is destructuring, https://github.com/applied-science/js-interop might be better since it works at compile time to emit code like I wrote in my prev message

isak16:04:49

Maybe that would be an idea for ClojureScript to add though. For example:

(fn [{:props [pressed]}] (js/console.log pressed))
= this JS:
({pressed}) => { console.log(pressed)} 

lilactown16:04:01

there's some subtleties when treating JS objects as data, but with enough caveats I can imagine that being quite useful

1
1
lilactown16:04:57

certainly the majority of code in helix (react wrapper) is making it nice to write by converting JS objects to/from maps in an efficient way 😄

😅 1
pinkfrog23:04:36

Thanks guys for the info!

pinkfrog23:04:45

Looks like the cljs-bean https://github.com/mfikes/cljs-bean#cljs-bean only provides the functionality of converting between js object and cljs map. Functionality-wise, any difference with the builtin cljs->js, js->cljs ones?

lilactown14:04:56

cljs->js and js->clj walk the entire structure and creates new cljs map/vector

lilactown14:04:00

cljs-bean creates a wrapper object that looks like a map or vector but in actuality keeps a reference to the underlying JS object and lazily accesses it on get et al.

👍 1
chef_kiss 1
isak16:04:49

Maybe that would be an idea for ClojureScript to add though. For example:

(fn [{:props [pressed]}] (js/console.log pressed))
= this JS:
({pressed}) => { console.log(pressed)} 

neumann23:04:06

I'm using extern inference and (set! *warn-on-infer* true) because I'm using advanced compilation. I'm trying to get the value property from a JS object. I have this code, but I always get a warning:

(defn obj-value
  [obj]
  (.-value ^js/Object obj))
The warning is:
Adding extern to Object for property value due to ambiguous expression (. obj -value)

  67  (defn obj-value
  68    [obj]
  69    (.-value ^js/Object obj))
        ^---
Does anyone know the "right" way to use the cljs built-in property access while avoiding that warning. I'm not asking how to use oops/oget. I'm already aware of that library. I'm trying to use the built-in cljs mechanism.

isak23:04:11

Using shadow-cljs, I'd do this:

(defn obj-value
  [obj]
  (.-value ^js obj))
or:
(defn obj-value
  [^js obj]
  (.-value obj))

neumann23:04:05

Ah yes. Good clarification. I'm using Figwheel + stock ClojureScript compiler. Let me try that and see.

neumann23:04:18

Yeah, I get the same warning...

Adding extern to Object for property value due to ambiguous expression (. obj -value)

  62  (defn obj-value
  63    [obj]
  64    (.-value ^js obj))
        ^---
If I recall, shadow-cljs treats ^js in a special way. This is where he talks about that: https://code.thheller.com/blog/shadow-cljs/2017/11/06/improved-externs-inference.html

1
neumann23:04:07

Interesting, putting anything except ^js makes the compilers stop warning. Some examples:

(.-value ^Object obj)
(.-value ^foobar obj)

neumann23:04:42

I think I've tried that before and it doesn't generate an extern.

p-himik00:04:49

^js is actually a CLJS thing. Maybe shadow-cljs does something in addition to what CLJS is doing, or maybe Figwheel does something on top of that. From CLJS' analyzer.cljc:

(defn js-tag? [x]
  (and (symbol? x)
       (or (= 'js x)
           (= "js" (namespace x)))))

neumann00:04:33

Good to know ^js is a cljs thing. I think the shadow-specific part is being able to leave off the part after js/ and not get a warning. At least that's my take on what @U05224H0W says: > By using the ^js tag metadata we are just telling the compiler that this is a native JS value and we are going to need externs for it. In shadow-cljs the /Foo.Bar type info is optional. You can still use it if you like but shadow-cljs won’t bother you if you don’t.

thheller05:04:11

yes, thats correct

p-himik07:04:51

What is the value of ^js in plain CLJS then, if it still gives off that warning?

thheller07:04:28

I don't know. When it comes to externs the Closure Compiler literally doesn't care which type it is on unless everything is typed

thheller07:04:36

since CLJS is untyped making the externs typed doesn't seem useful to me. maybe that should be changed, not my decision

thheller07:04:17

FWIW ^js is the same as typing ^js/ShadowJS basically. just putting everything on one type. I guess you could just write it out fully, no need to figure out actual types

p-himik07:04:35

@U050B88UR Do you recall why there's a warning for ^js/Object and consequently for ^js? The commit message just says "warn about externs to Object" without any details.

thheller07:04:03

in theory if you know the type of something and have externs for that type you can not rename those usages of x.foo() while other places can still rename x.foo()

thheller07:04:12

I'm guessing thats why

thheller07:04:24

if you put it on Object you never can rename it, thats why it makes sense in theory to be more specific with externs (and why all the default externs are typed)

thheller07:04:43

I'd argue that this doesn't matter and the only downside is that something doesn't get renamed when it maybe could.

p-himik07:04:06

Thanks! Hmm. I guess it makes sense to discourage people from adding ^js to forms that refer to JSON data that comes externally.

neumann15:04:53

@U05224H0W That's great to know that shadow-cljs ^js turns into ^js/ShadowJS! Thanks for the info! That reminds me of the https://clojurians.slack.com/archives/C07UQ678E/p1644954720630359 in #cljs-dev. In that case, the Google Closure Compiler had extern definitions for a built-in browser type, but a new method hadn't made it into the externs for the Closure Compiler version bundled with the cljs compiler. Because the cljs compiler found the externs, but the method I annotated was missing, the cljs compiler assumed I made a mistake and generated a warning. I see what you're saying about being "fully typed". In this case, the warning goes away with (.-value ^js/FooBar obj), so that must be because the "FooBar" type isn't known.

neumann15:04:10

@U05224H0W @U2FRKM4TW Thanks so much for your help and insight!