Typescript Like Intellisense for Clojure Functions With Malli

To some, it might come as a surprise that you can type Clojure functions and get static type-checking in your editor to warn you about invalid arguments, etc. This is not a feature of the language itself, it is possible via amazing open-source projects CLJ Kondo and Malli.

Similarly to typing Javascript code in Typescript, we can use separate type declaration files to instruct the TS compiler and linter. In Clojure, we can generate clj-kondo type configurations for our library or import these from library authors and enhance the developer experience.

In Malli, typing is similar to TS.

(ns core
  (:require [malli.core :as m]
            [malli.dev :as dev]))

(defn add [x y]
  (+ x y))

(m/=> add [:=>
           [:cat :int :int]
           :int])

;; start the instrumentation ~ type checking
(dev/start!)

Which generates the following type definitions in the clj-kondo cache that is picked up by the editor integration to provide type hints in your editor.

{:linters
 {:unresolved-symbol {:exclude [(malli.core/=>)]},
  :type-mismatch
  {:namespaces
   {core
    {add {:arities {2 {:args [:int :int], :ret :int}}}}}}}}

The type definition is fairly close to the TS ones

(m/=> add [:=>              ;; type is a function
           [:cat :int :int] ;; there's two arguments of type int
           :int             ;; the function returns an int
          ])
type add = (Number, Number) => Number;

When we have the instrumentation turned on

(add "2" 1) ;; evaluating this would result in following

-- Schema Error ------------------------------------------------

Invalid function arguments:

  ["2" 1]

Function Var:

  core/add

Input Schema:

  [:cat :int :int]

Errors:

  {:in [0], 
   :message "should be an integer", 
   :path [0], 
   :schema :int, 
   :value "2"}

More information:

  https://cljdoc.org/d/metosin/malli/CURRENT/doc/function-schemas

This can also be seen in the editor even before evaluating the code.

This week I was working on a small bug on how Malli generates CLJ Kondo type definitions and I thought it'd be a good moment to share some of the basics I picked up on while doing it. I haven't missed the types from TS in a long time but now I know how to configure these when needed.

Thanks for reading, I hope you found this useful.