While Clojure is dynamically typed, tools like clj-kondo, Cursive and clojure-lsp can offer some static analysis benefit like warning about undefined vars or functions. There isn't "true" static checking, but you can use Spec and Malli for runtime checking. That doesn't provide same level of compile-time guaranties as statically type language, yet it offers some unique capabilities that many statically typed languages struggle to match, like
- Dynamic predicates - Spec allows you to define types using arbitrary predicates, which can be more expressive than traditional static type systems;
- Runtime generative testing - Spec can automatically generate test data based on your specifications, which is powerful for property-based testing;
- Flexible validation - You can validate complex nested data structures and apply specs selectively, which is often more flexible than static type checking;
- Extensibility - Specs can be added to existing types without modifying their source, and data-driven nature of it - Specs are just data and can be manipulated programmatically.