TypedStructor
is a library for defining structs with types effortlessly.
(This library is a rewritten version of TypedStruct because it is no longer actively maintained.)
Add :typed_structor
to the list of dependencies in mix.exs
:
def deps do
[
{:typed_structor, "~> 0.1"}
]
end
Add :typed_structor
to your .formatter.exs
file
[
# import the formatter rules from `:typed_structor`
import_deps: [..., :typed_structor],
inputs: [...]
]
To define a struct with types, use TypedStructor
,
and then define fields under the TypedStructor.typed_structor/2
macro,
using the TypedStructor.field/3
macro to define each field.
defmodule User do
# use TypedStructor to import the `typed_structor` macro
use TypedStructor
typed_structor do
# Define each field with the `field` macro.
field :id, pos_integer()
# set a default value
field :name, String.t(), default: "Unknown"
# enforce a field
field :age, non_neg_integer(), enforce: true
end
end
This is equivalent to:
defmodule User do
defstruct [:id, :name, :age]
@type t() :: %__MODULE__{
id: pos_integer() | nil,
# Note: The 'name' can not be nil, for it has a default value.
name: String.t(),
age: non_neg_integer()
}
end
Check TypedStructor.typed_structor/2
and TypedStructor.field/3
for more information.
You can also generate an opaque
type for the struct,
even changing the type name:
defmodule User do
use TypedStructor
typed_structor type_kind: :opaque, type_name: :profile do
field :id, pos_integer()
field :name, String.t()
field :age, non_neg_integer()
end
end
This is equivalent to:
defmodule User do
use TypedStructor
defstruct [:id, :name, :age]
@opaque profile() :: %__MODULE__{
id: pos_integer() | nil,
name: String.t() | nil,
age: non_neg_integer() | nil
}
end
Type parameters also can be defined:
defmodule User do
use TypedStructor
typed_structor do
parameter :id
parameter :name
field :id, id
field :name, name
field :age, non_neg_integer()
end
end
becomes:
defmodule User do
@type t(id, name) :: %__MODULE__{
id: id | nil,
name: name | nil,
age: non_neg_integer() | nil
}
defstruct [:id, :name, :age]
end
If you prefer to define a struct in a submodule, pass the module
option.
defmodule User do
use TypedStructor
# `%User.Profile{}` is generated
typed_structor module: Profile do
field :id, pos_integer()
field :name, String.t()
field :age, non_neg_integer()
end
end
You can define the type only without defining the struct,
it is useful when the struct is defined by another library(like Ecto.Schema
).
defmodule User do
use Ecto.Schema
use TypedStructor
typed_structor define_struct: false do
field :id, pos_integer()
field :name, String.t()
field :age, non_neg_integer(), default: 0
end
schema "users" do
field :name, :string
field :age, :integer, default: 0
end
end
To add a @typedoc
to the struct type, just add the attribute in the typed_structor block:
typed_structor do
@typedoc "A typed user"
field :id, pos_integer()
field :name, String.t()
field :age, non_neg_integer()
end
You can also document submodules this way:
typedstructor module: Profile do
@moduledoc "A user profile struct"
@typedoc "A typed user profile"
field :id, pos_integer()
field :name, String.t()
field :age, non_neg_integer()
end
TypedStructor
offers a plugin system to enhance functionality.
For details on creating a plugin, refer to the TypedStructor.Plugin
module.
Here is a example of TypedStructor.Plugins.Accessible
plugin to define Access
behavior for the struct.
defmodule User do
use TypedStructor
typed_structor do
plugin TypedStructor.Plugins.Accessible
field :id, pos_integer()
field :name, String.t()
field :age, non_neg_integer()
end
end
user = %User{id: 1, name: "Phil", age: 20}
get_in(user, [:name]) # => "Phil"