I think dynamic typing is a lot more problematic in imperative/OO languages. One problem is that the data is mutable, and you pass things around by reference. Even if you knew the shape of the data originally, there's no way to tell whether it's been changed elsewhere via side effects. The other problem is that OO encourages proliferation of types in your code. Keeping track of that quickly gets out of hand.
Clojure embraces immutability, and any changes to the data happen explicitly in a known context. This makes it much easier to know exactly what the shape of the data is at any one place in your application.
Meanwhile, all the data is structured using a set of common data structures. Any iterator function such as `map`, `filter`, or `reduce` can iterate any data structure, and it's completely agnostic regarding the concrete types. The code that cares about the types is passed in as a parameter. Pretty much any data transformations in Clojure are accomplished by chaining functions from the standard library together, with domain specific code bubbling up to a shallow layer at the top.
There's also the culture of using Spec and Schema in Clojure, so you often have assertions at the interface level. For example, there's ring-spec https://github.com/ring-clojure/ring-spec/blob/master/src/ri... providing the specification for Ring.