Elixir 1.20 introduces gradual type checking for every program, completing the first development milestone in a project that began as a research effort in 2022. The release performs inference without requiring developers to add type annotations, allowing existing code to be examined for dead paths and type violations.
The maintainers distinguish those violations from warnings based on style or likelihood. Their stated goal is to report operations that are guaranteed to fail at runtime if executed, while keeping false positives extremely low. The system is based on set-theoretic types built from unions, intersections and negations, an approach described in a research paper published in 2023.
A central feature is Elixir's `dynamic()` type. In many gradual systems, an `any` type can effectively stop useful checking because all operations are permitted. Elixir instead treats a dynamic value as a range of runtime possibilities that can be narrowed as the value moves through a program. A warning is produced when the inferred possibilities and an operation's accepted inputs do not overlap.
For example, if a value can only be an integer or a binary, using it with an operation that accepts numbers does not immediately produce a violation because the integer case remains valid. Passing the same value to a function that requires a map is disjoint from both possibilities, so the checker can flag it. This compatibility rule is meant to preserve valid dynamic code while finding definite errors.
Narrowing provides additional precision. When code accesses fields and applies numeric operations, the checker can refine an initially dynamic input into a map with specific numeric keys. A later attempt to use that map itself as a number can then be identified as invalid. The system also infers information from guards, tuple-size tests, map keys and list structure.
Elixir reported passing 12 of 13 categories in the “If T” type-narrowing benchmark. That result is supplied by the project and does not by itself characterize performance on every codebase, but it illustrates the intended recovery of useful type information from unannotated programs.
User-written type annotations are planned for a later phase. Until then, version 1.20 focuses on extracting safety signals with little migration work. The effort grew from a partnership with France's CNRS and Remote; current development sponsorship comes from Fresha and Tidewave. The release therefore changes compiler analysis immediately while leaving the language's explicit static-typing surface for future milestones.\n\nAdopting inference before annotations also gives maintainers a way to observe the checker against real projects without forcing source changes. Developers can evaluate whether diagnostics are understandable and actionable, while library authors retain compatibility with dynamic calling patterns. Feedback from that period can shape the later annotation design and determine how teams introduce stricter boundaries.


