NewsProgramming Languages

Elixir 1.20 Gradual Typing: No Annotations, Real Bug Catches

Elixir 1.20 gradual type system visualization with abstract nodes and type inference arrows

Elixir v1.20 dropped today, and José Valim’s release post says something the language has never claimed before: Elixir is now a gradually typed language. The compiler performs type inference across your entire codebase without you touching a line of code. No annotations, no @spec tags, no migration work — your existing code gets type analysis for free, and every warning it surfaces represents a confirmed runtime failure, not a “might be worth checking.”

What Actually Changed in Elixir 1.20

Type inference in Elixir isn’t new — the compiler has been building toward this since 1.17. What 1.20 adds is completeness: the system now covers all language constructs, including complex boolean guards, map key domains, and cross-dependency inference. The majority of functions in Elixir’s Map module are now typed in the standard library. The engine builds a control-flow graph of your code, tracks types through every branch and pattern match, and narrows its understanding as it accumulates evidence. Per the official type system documentation, the system is designed to be sound by construction — when it identifies a violation, the code is provably broken.

The practical result: run mix compile on your existing codebase after upgrading and review the new warnings. Each one is a confirmed bug — not a style issue, not a speculative “this might fail.” Some of what it catches:

  • Dead code in case branches — clauses that can never execute because an earlier branch already handles the type
  • Struct field mismatches — passing a wrong-shaped map to a function expecting a specific struct
  • Impossible comparisons — comparing values from structurally incompatible types
  • Missing protocol implementations — interpolating a struct that doesn’t implement String.Chars

The Key Difference: dynamic() Is Not any()

Here’s where Elixir’s Elixir 1.20 gradual typing approach diverges from every other gradual typing system you’ve encountered. Python’s Any and TypeScript’s any are full opt-outs — they disable type checking entirely and spread silently through codebases. Elixir uses dynamic() instead, which acts as a range of types rather than a void. If a variable is dynamic(integer() or binary()), the compiler still enforces that at least one of those types must be compatible with how you use it. It narrows as evidence accumulates through guards and pattern matches.

The system is also sound — meaning its type theory is backed by set-theoretic semantics where types are actual sets of values, composed via unions, intersections, and negations. When the compiler flags a violation, it has proven the code is broken, not guessed. This is a harder guarantee than Python’s mypy or TypeScript offer. The set-theoretic deep dive from Gabriel Ortuno explains how this plays out across the 1.17–1.20 evolution — if you want the theory behind the approach, it’s worth reading.


# Pattern matching gives the compiler type evidence
def update(%User{} = user, name) do
  %{user | name: name}  # Compiler verifies user is %User{} throughout
end

# Without the pattern match, the compiler can't verify the struct shape.
# Adding %User{} = user is now "load-bearing" from a type perspective.

Related: TypeScript 7: Go Compiler, Breaking Changes, Migration Guide

Dialyzer Is Done

If you’ve tried Dialyzer and given up, you’re not alone. The ElixirForum discussion on Dialyzer shows a consistent community consensus: it requires manual @spec annotations for useful output, runs slowly in CI, and generates enough noise that most teams stop trusting it. The payoff rarely justified the investment.

The 1.20 type system addresses all three problems. No annotations required. Integrated into mix compile, not a separate slow pass. And because it’s sound, there are no false positives in the core cases — if it warns, something is actually wrong. For most Elixir teams, Dialyzer is now the older, noisier solution to a problem the compiler handles directly.

Dynamic Languages Are Picking a Side

Elixir joins a clear trend. TypeScript is now the top language on GitHub — not JavaScript, TypeScript. Python type hints are standard practice in production codebases. PHP 8.x ships fully strict typing. Ruby has Sorbet and Steep. The industry has made its call: dynamic languages that want to work at scale need gradual type systems. As detailed in the January 2026 roadmap post, this 1.20 release completes a 15-month plan that was public and methodical — this didn’t happen overnight.

Elixir’s contribution to this trend is the most theoretically rigorous implementation of the group. Set-theoretic types handle compositional edge cases that structural type systems (TypeScript) and annotation-only systems (Python mypy) struggle with. Moreover, the “zero annotation tax” is practically significant — Python’s gradual typing still requires annotating your way to safety over time, while Elixir’s inference works from code that already exists. User-defined type signatures are still pending in 1.20, but for catching existing bugs today, you don’t need to wait.

Key Takeaways

  • Elixir v1.20 is officially a gradually typed language — the compiler infers types across all constructs without requiring @spec annotations
  • Upgrade and run mix compile: every new warning is a confirmed runtime bug, not a style suggestion
  • dynamic() is not any() — it narrows through guards and pattern matches, giving partial type safety even in untyped code
  • Dialyzer is effectively superseded for most use cases; the built-in system requires less setup and generates less noise
  • Pattern matching with explicit struct patterns (%MyStruct{} = var) now provides the compiler evidence it uses throughout the function — make this a habit
ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *

    More in:News