You can spec a map by specifying which keys should be present in the map:
(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)
(clojure.spec/def ::person (clojure.spec/keys :req [::name ::age ::occupation]))
(clojure.spec/valid? ::person {::name "john" ::age 25 ::occupation "programmer"})
;; => true
:req
is a vector of keys required to be present in the map. You can specify additional options such as :opt
, a vector of keys which are optional.
The examples so far require that the keys in the name are namespace-qualified. But it's common for map keys to be unqualified. For this case, clojure.spec
provides :req and :opt equivalents for unqualified keys: :req-un
and :opt-un
. Here's the same example, with unqualified keys:
(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)
(clojure.spec/def ::person (clojure.spec/keys :req-un [::name ::age ::occupation]))
(clojure.spec/valid? ::person {:name "john" :age 25 :occupation "programmer"})
;; => true
Notice how the specs provided in the :req-un
vector as still qualified. clojure.spec, will automatically confirm the unqualified versions in the map when conforming the values.
namespace map literal syntax allows you to qualify all the keys of a map by a single namespace succinctly. For example:
(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)
(clojure.spec/def ::person (clojure.spec/keys :req [::name ::age ::occupation]))
(clojure.spec/valid? ::person #:user{:name "john" :age 25 :occupation "programmer"})
;;=> true
Notice the special #:
reader syntax. We follow this with the namespace we wish to qualify all the map keys by. These will then be checked against the specs corresponding to the provided namespace.