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.