TypeStruct defines structs and their types together.
Add :type_struct to your dependencies:
def deps do
[
{:type_struct, "~> 1.0"}
]
enddefmodule Point do
use TypeStruct
defstruct x: integer,
y: integer
endTypeStruct can also define nested struct modules:
defmodule Accounts do
use TypeStruct
defstruct User,
id: integer,
name: String.t() \\ "",
eye_color: :black | :blue | :brown | :green
defstruct Group,
id: integer,
name: String.t(),
users: [User.t()] \\ []
endEach field is written as name: type. A default can be given with \\:
defstruct User, name: String.t() \\ ""Fields without defaults are required via @enforce_keys:
defstruct User, id: integer, name: String.t() \\ ""If you want a field to default to nil, declare that default explicitly:
defstruct User, email: String.t() | nil \\ nilBy default, TypeStruct defines a public t/0 type for every struct:
defmodule Accounts do
use TypeStruct
defstruct User, id: integer
endThe Accounts.User example above is equivalent to:
defmodule Accounts.User do
defstruct [:id]
@type t() :: %__MODULE__{id: integer}
endYou can choose a different type attribute or name:
defstruct User, type(fields), id: integer
# @type fields() :: %__MODULE__{id: integer}
defstruct User, typep(fields), id: integer
# @typep fields() :: %__MODULE__{id: integer}
defstruct User, opaque(t), id: integer
# @opaque t() :: %__MODULE__{id: integer}The field types are regular Elixir typespecs. Local types referenced by nested structs are resolved against the parent module, while built-in types are left untouched:
defmodule Catalog do
use TypeStruct
@type color :: :red | :green | :blue
defstruct Product,
color: color,
tags: nonempty_list(String.t())
endHere color means Catalog.color/0, while nonempty_list/1 is the built-in
type.
A @typedoc placed immediately before a nested defstruct is moved into the
generated module:
defmodule Accounts do
use TypeStruct
@typedoc "A user in the current account."
defstruct User, id: integer
end