Elixir es un lenguaje de tipado dinámico, lo que significa que no proporciona verificaciones de tipos en tiempo de compilación. Aun así, las especificaciones de tipos pueden ser útiles porque:
Se puede agregar una especificación de tipos a una función usando el atributo de módulo @spec justo antes de la definición de la función.
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
Expresiones permitidas en una especificación de tipos:
Tipos básicos, por ejemplo:
boolean()integer(), non_neg_integer(), pos_integer(), float()
list()map()any()Una unión de tipos:
integer() | list(integer())
Tipos parametrizados:
list(integer()): una lista de enteros%{age: integer()}: un mapa con un valor entero en la clave :age
Tipos remotos (definidos en algún módulo), por ejemplo:
Literales, por ejemplo:
%{}: un mapa vacío[]: una lista vacía (pero [any()] es una lista no vacía):ok: un literal de átomoTipos especializados incorporados, por ejemplo:
char(): un entero del rango 0..0x10FFFF
charlist(): una lista de caractereskeyword(): una lista de tuplas de dos elementos, donde el primer elemento es un átomoTipos personalizados
Puedes encontrar una lista completa de todos los tipos en la sección «Typespecs» de la documentación oficial.
Los argumentos en la especificación de tipos también se pueden nombrar, lo cual es útil para distinguir varios argumentos del mismo tipo.
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
Los tipos personalizados se pueden definir usando uno de estos tres atributos de módulo:
@type: define un tipo público@typep: define un tipo privado@opaque: define un tipo público cuya estructura es privada@type color :: {hue :: integer, saturation :: integer, lightness :: integer}
@spec to_hex(color()) :: String.t()
String.t() vs binary() vs string()
String.t() es el tipo correcto para usar con los strings de Elixir, que son binarios codificados en UTF-8. Técnicamente, String.t() se define como un binary(), y esos dos tipos son equivalentes para las herramientas de análisis, pero String.t() es la opción que revela mejor la intención al documentar funciones que trabajan con strings de Elixir.
Por otro lado, string() es un tipo diferente. Es un string de Erlang, conocido en Elixir como charlist. El tipo string() se debe evitar en las especificaciones de tipos y se debe usar charlist() en su lugar.
Dialyzer es una herramienta de análisis estático que puede detectar problemas como errores de tipos en código Erlang y Elixir. La forma más fácil de usar Dialyzer en un proyecto de Elixir es con Dialyxir.