Elixir é uma linguagem de tipagem dinâmica, o que significa que ela não faz verificações de tipos em tempo de compilação. Mesmo assim, as especificações de tipo podem ser úteis porque:
Você pode adicionar uma especificação de tipo a uma função usando o atributo de módulo @spec logo 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 em uma 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()} - 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 embutidos, por exemplo:
char() - um inteiro do intervalo 0..0x10FFFF
charlist() - uma lista de charskeyword() - uma lista de tuplas de dois elementos, onde o primeiro elemento é um átomoTipos personalizados
Uma lista completa de todos os tipos está na seção "Typespecs" da documentação oficial.
Os argumentos na especificação de tipo também podem ser nomeados. Isso é útil para distinguir vários argumentos do mesmo tipo.
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
Tipos personalizados podem ser definidos com um dos 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()
String.t() é o tipo correto a usar para strings do Elixir, que são binários codificados em UTF-8. Tecnicamente, String.t() é definida como um binary(), e esses dois tipos são equivalentes para as ferramentas de análise. Ainda assim, 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 em especificações de tipo, e charlist() deve ser usado no lugar.
O Dialyzer é uma ferramenta de análise estática que consegue detectar problemas como erros de tipo em código Erlang e Elixir. A forma mais fácil de usar o Dialyzer em um projeto Elixir é com o Dialyxir.