O Elixir é uma linguagem com tipos dinâmicos, o que significa que não faz verificações de tipos em tempo de compilação. Ainda assim, as especificações de tipo podem ser úteis porque:
Podes adicionar uma especificação de tipo a uma função com o atributo de módulo @spec, mesmo antes da definição da função.
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
Expressões permitidas numa especificação de tipo:
Tipos básicos, por exemplo:
boolean()integer(), non_neg_integer(), pos_integer(), float()
list()map()any()Uma união de tipos:
integer() | list(integer())
Tipos parametrizados:
list(integer()), uma lista de inteiros%{age: integer()}, um mapa com um valor inteiro sob a chave :age
Tipos remotos (definidos em algum módulo), por exemplo:
Literais, por exemplo:
%{}, um mapa vazio[], uma lista vazia (mas [any()] é uma lista não vazia):ok, um literal de átomoTipos especializados incorporados, por exemplo:
char(), um inteiro do intervalo 0..0x10FFFF
charlist(), uma lista de caratereskeyword(), uma lista de tuplos de dois elementos, em que o primeiro elemento é um átomoTipos personalizados
Encontras a lista completa de todos os tipos na secção "Typespecs" da documentação oficial.
Os argumentos da especificação de tipo também podem ter nomes, o que é útil para distinguir vários argumentos do mesmo tipo.
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
Podes definir tipos personalizados com um destes três atributos de módulo:
@type, define um tipo público@typep, define um tipo privado@opaque, define um tipo público cuja estrutura é privada@type color :: {hue :: integer, saturation :: integer, lightness :: integer}
@spec to_hex(color()) :: String.t()
String.t() vs binary() vs string()
O String.t() é o tipo correto a usar para as strings do Elixir, que são binários codificados em UTF-8. Tecnicamente, String.t() é definido como um binary(), e esses dois tipos são equivalentes para as ferramentas de análise, mas String.t() é a escolha que revela melhor a intenção ao documentar funções que trabalham com strings do Elixir.
Por outro lado, string() é um tipo diferente. É uma string do Erlang, conhecida no Elixir como charlist. O tipo string() deve ser evitado nas especificações de tipo; em vez dele, deves usar charlist().
O Dialyzer é uma ferramenta de análise estática que consegue detetar problemas como erros de tipo em código Erlang e Elixir. A forma mais fácil de usar o Dialyzer num projeto de Elixir é com o Dialyxir.