Elixir类型规范 #

一、类型规范概述 #

1.1 什么是类型规范 #

类型规范是函数的类型签名,用于文档和静态分析。

1.2 为什么使用类型规范 #

  • 提供文档
  • 静态分析
  • IDE支持
  • 代码质量

二、基本类型 #

2.1 内置类型 #

elixir
any()
atom()
binary()
bitstring()
boolean()
byte()
char()
float()
function()
integer()
list()
map()
number()
pid()
port()
reference()
tuple()

2.2 字面量类型 #

elixir
:ok
:error
nil
true
false

2.3 集合类型 #

elixir
list(type)
nonempty_list(type)
maybe_improper_list(type1, type2)
[type]
[type, ...]

2.4 元组类型 #

elixir
tuple()
{type1, type2, ...}
{:ok, term()}
{:error, atom()}

2.5 映射类型 #

elixir
map()
%{key_type => value_type}
%{required_key: type, optional_key?: type}

2.6 函数类型 #

elixir
(... -> type)
(type1, type2 -> type)

三、@spec #

3.1 基本语法 #

elixir
@spec function_name(param_types) :: return_type

3.2 示例 #

elixir
defmodule Math do
  @spec add(number(), number()) :: number()
  def add(a, b), do: a + b

  @spec divide(number(), number()) :: {:ok, number()} | {:error, :division_by_zero}
  def divide(_, 0), do: {:error, :division_by_zero}
  def divide(a, b), do: {:ok, a / b}

  @spec factorial(non_neg_integer()) :: pos_integer()
  def factorial(0), do: 1
  def factorial(n), do: n * factorial(n - 1)
end

3.3 多个子句 #

elixir
defmodule String do
  @spec split(binary()) :: [binary()]
  @spec split(binary(), binary()) :: [binary()]
  @spec split(binary(), binary(), keyword()) :: [binary()]
  def split(string, pattern \\ " ", opts \\ [])
end

四、@type #

4.1 定义类型 #

elixir
defmodule User do
  @type id :: integer()
  @type name :: String.t()
  @type email :: String.t()

  @type t :: %__MODULE__{
    id: id(),
    name: name(),
    email: email()
  }

  defstruct [:id, :name, :email]
end

4.2 使用自定义类型 #

elixir
defmodule UserService do
  @spec create(User.name(), User.email()) :: User.t()
  def create(name, email) do
    %User{name: name, email: email}
  end

  @spec find(User.id()) :: User.t() | nil
  def find(id) do
    # ...
  end
end

4.3 @typep #

私有类型:

elixir
defmodule Internal do
  @typep internal_id :: integer()

  @spec process(internal_id()) :: :ok
  def process(id), do: :ok
end

4.4 @opaque #

不透明类型:

elixir
defmodule Handle do
  @opaque t :: reference()

  @spec new() :: t()
  def new, do: make_ref()
end

五、约束 #

5.1 guard约束 #

elixir
@spec pos_number(number()) :: number() when number: number()
@spec my_func(arg) :: result when arg: integer(), result: float()

5.2 使用示例 #

elixir
defmodule Bounded do
  @type bounded(a) :: a when a: number()

  @spec in_range(bounded(number()), number(), number()) :: boolean()
  def in_range(value, min, max) do
    value >= min and value <= max
  end
end

六、行为规范 #

6.1 @callback #

elixir
defmodule Parser do
  @callback parse(String.t()) :: {:ok, term()} | {:error, term()}
  @callback format(term()) :: String.t()
end

6.2 @macrocallback #

elixir
defmodule MyBehaviour do
  @macrocallback my_macro(term()) :: Macro.t()
end

6.3 实现行为 #

elixir
defmodule JsonParser do
  @behaviour Parser

  @impl true
  def parse(string), do: Jason.decode(string)

  @impl true
  def format(data), do: Jason.encode(data)
end

七、Dialyzer #

7.1 安装 #

Dialyzer是Erlang/OTP的一部分,无需额外安装。

7.2 配置 #

elixir
# mix.exs
def project do
  [
    app: :my_app,
    version: "0.1.0",
    dialyzer: [
      plt_add_apps: [:mix],
      flags: [:unmatched_returns, :error_handling, :race_conditions]
    ]
  ]
end

7.3 运行 #

bash
mix dialyzer

7.4 常见警告 #

  • no_return - 函数没有返回
  • no_unused - 未使用的变量
  • pattern_match - 模式匹配问题
  • type_error - 类型错误

八、类型规范最佳实践 #

8.1 完整示例 #

elixir
defmodule Bank.Account do
  @type id :: pos_integer()
  @type balance :: non_neg_integer()
  @type currency :: :USD | :EUR | :GBP

  @type t :: %__MODULE__{
    id: id(),
    balance: balance(),
    currency: currency()
  }

  defstruct [:id, :balance, :currency]

  @spec new(currency()) :: t()
  def new(currency \\ :USD) do
    %__MODULE__{id: generate_id(), balance: 0, currency: currency}
  end

  @spec deposit(t(), pos_integer()) :: t()
  def deposit(%__MODULE__{balance: balance} = account, amount) do
    %{account | balance: balance + amount}
  end

  @spec withdraw(t(), pos_integer()) :: {:ok, t()} | {:error, :insufficient_funds}
  def withdraw(%__MODULE__{balance: balance} = account, amount) do
    if balance >= amount do
      {:ok, %{account | balance: balance - amount}}
    else
      {:error, :insufficient_funds}
    end
  end

  @spec balance(t()) :: balance()
  def balance(%__MODULE__{balance: balance}), do: balance

  defp generate_id, do: :rand.uniform(1_000_000)
end

九、总结 #

本章学习了:

特性 用途
@spec 函数类型规范
@type 公开类型定义
@typep 私有类型定义
@opaque 不透明类型
@callback 行为回调规范
Dialyzer 静态分析工具

准备好学习框架了吗?让我们进入下一章。

最后更新:2026-03-27