Announcement: https://elixir-lang.org/blog/2026/06/03/elixir-v1-20-0-released/
This release requires Erlang/OTP 27+ and is compatible with Erlang/OTP 29.
Type system improvements
Elixir’s type system now understands all language constructs and can infer types for your function definitions, using typing information from Elixir’s standard library and your dependencies, to find verified bugs and dead code.
This has been achieved through a series of improvements, such as type refinement across clauses, occurrence typing, typing of map keys and domains, and more.
Type inference of guards
This release also performs inference of guards! Let’s see some examples:
def example(x, y) when is_list(x) and is_integer(y)
The code above correctly infers x is a list and y is an integer.
def example({:ok, x} = y) when is_binary(x) or is_integer(x)
The one above infers x is a binary or an integer, and y is a two element tuple with :ok as first element and a binary or integer as second.
def example(x) when is_map_key(x, :foo)
The code above infers x is a map which has the :foo key, represented as %{..., foo: dynamic()}. Remember the leading ... indicates the map may have other keys.
def example(x) when not is_map_key(x, :foo)
And the code above infers x does not have the :foo key (hence x.foo will raise a typing violation), which has the type: %{..., foo: not_set()}.
You can also have expressions that assert on the size of data structures:
def example(x) when tuple_size(x) < 3
Elixir will correctly track the tuple has at most two elements, and therefore accessing elem(x, 3) will emit a typing violation. In other words, Elixir can look at complex guards, infer types, and use this information to find bugs in our code, without a need to introduce type signatures (yet).
Whole-body type inference
Elixir also performs …