View Source TypedStructor (TypedStructor v0.4.2)
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.)
Installation
Add :typed_structor to the list of dependencies in mix.exs:
def deps do
[
{:typed_structor, "~> 0.4"}
]
endAdd :typed_structor to your .formatter.exs file
[
# import the formatter rules from `:typed_structor`
import_deps: [..., :typed_structor],
inputs: [...]
]Usage
General usage
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
endThis 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()
}
endCheck TypedStructor.typed_structor/2 and TypedStructor.field/3 for more information.
Options
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
endThis 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
}
endType 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
endbecomes:
defmodule User do
@type t(id, name) :: %__MODULE__{
id: id | nil,
name: name | nil,
age: non_neg_integer() | nil
}
defstruct [:id, :name, :age]
endIf you prefer to define a struct in a submodule, you can use
the module option with TypedStructor. This allows you to
encapsulate the struct definition within a specific submodule context.
Consider this example:
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
endWhen defining a struct in a submodule, the typed_structor block
functions similarly to a defmodule block. Therefore,
the previous example can be alternatively written as:
defmodule User do
defmodule Profile do
use TypedStructor
typed_structor do
field :id, pos_integer()
field :name, String.t()
field :age, non_neg_integer()
end
end
endFurthermore, the typed_structor block allows you to
define functions, derive protocols, and more, just
as you would within a defmodule block. Here's a example:
defmodule User do
use TypedStructor
typed_structor module: Profile, define_struct: false do
@derive {Jason.Encoder, only: [:email]}
field :email, String.t()
use Ecto.Schema
@primary_key false
schema "users" do
Ecto.Schema.field(:email, :string)
end
import Ecto.Changeset
def changeset(%__MODULE__{} = user, attrs) do
user
|> cast(attrs, [:email])
|> validate_required([:email])
end
end
endNow, you can interact with these structures:
iex> User.Profile.__struct__()
%User.Profile{__meta__: #Ecto.Schema.Metadata<:built, "users">, email: nil}
iex> Jason.encode!(%User.Profile{})
"{\"email\":null}"
iex> User.Profile.changeset(%User.Profile{}, %{"email" => "my@email.com"})
#Ecto.Changeset<
action: nil,
changes: %{email: "my@email.com"},
errors: [],
data: #User.Profile<>,
valid?: true
>Documentation
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()
endYou 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()
endPlugins
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 Guides.Plugins.Accessible plugin to define Access behavior for the struct.
defmodule User do
use TypedStructor
typed_structor do
plugin Guides.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"Plugins guides
Here are some Plugin Guides for creating your own plugins. Please check them out and feel free to copy-paste the code.
Summary
Functions
Defines a field in a typed_structor/2.
You can override the options set by typed_structor/2 by passing options.
Defines a type parameter in a typed_structor/2.
Registers a plugin for the currently defined struct.
Defines a struct with type information.
Functions
Defines a field in a typed_structor/2.
You can override the options set by typed_structor/2 by passing options.
Example
# A field named :example of type String.t()
field :example, String.t()Options
:default- sets the default value for the field:enforce- if set totrue, enforces the field, and makes its type non-nullable if:defaultis not set
How
:defaultand:enforceaffecttypeand@enforce_keys
:default:enforcetype@enforce_keyssettruet()excludedsetfalset()excludedunsettruet()includedunsetfalset() | nilexcluded
Defines a type parameter in a typed_structor/2.
Example
# A type parameter named int
parameter :int
fied :number, int # not int()
Registers a plugin for the currently defined struct.
Example
typed_structor do
plugin MyPlugin
field :string, String.t()
endFor more information on how to define your own plugins, please see
TypedStructor.Plugin. To use a third-party plugin, please refer directly to
its documentation.
Defines a struct with type information.
Inside a typed_structor block, you can define fields with the field/3 macro.
Options
:module- if provided, a new submodule will be created with the struct.:enforce- iftrue, the struct will enforce the keys, seefield/3options for more information.:define_struct- iffalse, the type will be defined, but the struct will not be defined. Defaults totrue.:type_kind- the kind of type to use for the struct. Defaults totype, can beopaqueortypep.:type_name- the name of the type to use for the struct. Defaults tot.
Examples
defmodule MyStruct do
use TypedStructor
typed_structor do
field :name, String.t()
field :age, integer()
end
endCreates the struct in a submodule instead:
defmodule MyStruct do
use TypedStructor
typed_structor module: Struct do
field :name, String.t()
field :age, integer()
end
endTo add a @typedoc to the struct type and @moduledoc to the submodule,
just add the module attribute in the typed_structor block:
defmodule MyStruct do
use TypedStructor
typed_structor module: Struct do
@typedoc "A typed struct"
@moduledoc "A submodule"
field :name, String.t()
field :age, integer()
end
end