atom, binary, boolean, function, list, map, pid, reference, integer, float, and tuple
There are a few others but they are generally special cases of the ones above. Having so few data types tends to make it much more obvious what you’re working with and what operations are available. Additionally, because behavior is completely separate from data it’s infinitely easier to know what you can and can’t do with a given value.
Ruby being dynamic drove me insane at times, but Elixir/Erlang being dynamic has been a boon to productivity and quality of life. I recently had to write some TypeScript and was losing my mind fighting the compiler, even though I knew at runtime everything would be fine. Eventually I slathered enough “any” on to make the burning stop… But! That’s something I haven’t had to do in years, and it was 100% due to type system chicanery and not preventing a bug or making the underlying code more sound.
There are still some occasions where having some static typing would be nice— but they’re pretty rare and often only for things that are extremely critical or expensive to fix. And IMHO even in those cases, Elixir’s clarity and lack of (implicit) state generally make up for it.