{"id":13984353,"url":"https://github.com/elixir-typed-structor/typed_structor","last_synced_at":"2026-03-07T07:05:26.041Z","repository":{"id":246545050,"uuid":"821423406","full_name":"elixir-typed-structor/typed_structor","owner":"elixir-typed-structor","description":"TypedStructor is a library for defining typed structs, exceptions and record macros with effortlessly.","archived":false,"fork":false,"pushed_at":"2025-07-31T13:35:01.000Z","size":104,"stargazers_count":18,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-07T01:21:16.443Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/typed_structor","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elixir-typed-structor.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-06-28T14:01:17.000Z","updated_at":"2025-10-06T09:28:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"ce9054c2-232e-46b8-b51c-cdedee65b9f9","html_url":"https://github.com/elixir-typed-structor/typed_structor","commit_stats":null,"previous_names":["elixir-typed-structor/typed_structor"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/elixir-typed-structor/typed_structor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-typed-structor%2Ftyped_structor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-typed-structor%2Ftyped_structor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-typed-structor%2Ftyped_structor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-typed-structor%2Ftyped_structor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elixir-typed-structor","download_url":"https://codeload.github.com/elixir-typed-structor/typed_structor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-typed-structor%2Ftyped_structor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280316652,"owners_count":26309993,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-09T06:00:39.450Z","updated_at":"2026-03-07T07:05:26.025Z","avatar_url":"https://github.com/elixir-typed-structor.png","language":"Elixir","funding_links":[],"categories":["Macros"],"sub_categories":[],"readme":"# TypedStructor\n\n[![Build Status](https://github.com/elixir-typed-structor/typed_structor/actions/workflows/elixir.yml/badge.svg)](https://github.com/elixir-typed-structor/typed_structor/actions/workflows/elixir.yml)\n[![Hex.pm](https://img.shields.io/hexpm/v/typed_structor)](https://hex.pm/packages/typed_structor)\n[![HexDocs](https://img.shields.io/badge/HexDocs-gray)](https://hexdocs.pm/typed_structor)\n[![Plugin guides](https://img.shields.io/badge/plugin_guides-indianred?label=%F0%9F%94%A5\u0026labelColor=snow)](https://hexdocs.pm/typed_structor/introduction.html)\n\nTypedStructor eliminates the boilerplate of defining Elixir structs, type specs, and enforced keys separately. Define them once, keep them in sync automatically.\n\n**Before** -- three declarations that must stay in sync manually:\n\n```elixir\ndefmodule User do\n  @enforce_keys [:id]\n  defstruct [:id, :name, :age]\n\n  @type t() :: %__MODULE__{\n    id: pos_integer(),\n    name: String.t() | nil,\n    age: non_neg_integer() | nil\n  }\nend\n```\n\n**After** -- a single source of truth:\n\n```elixir\ndefmodule User do\n  use TypedStructor\n\n  typed_structor do\n    field :id, pos_integer(), enforce: true\n    field :name, String.t()\n    field :age, non_neg_integer()\n  end\nend\n```\n\n## Feature Highlights\n\n- **Single definition** -- struct, type spec, and `@enforce_keys` generated from one block\n- **Nullable by default** -- unenforced fields without defaults automatically include `| nil`\n- **Fine-grained null control** -- override nullability per-field or per-block with the `:null` option\n- **Opaque and custom types** -- generate `@opaque`, `@typep`, or rename the type from `t()`\n- **Type parameters** -- define generic/parametric types\n- **Multiple definers** -- supports structs, exceptions, and Erlang records\n- **Plugin system** -- extend behavior at compile time with composable plugins\n- **Nested modules** -- define structs in submodules with the `:module` option\n\n\u003c!-- MODULEDOC --\u003e\n\n## Installation\n\nAdd `:typed_structor` to your dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:typed_structor, \"~\u003e 0.6\"}\n  ]\nend\n```\n\n\u003e #### Formatter Setup {: .tip}\n\u003e\n\u003e Add `:typed_structor` to your `.formatter.exs` for proper indentation:\n\u003e\n\u003e ```elixir\n\u003e [\n\u003e   import_deps: [..., :typed_structor],\n\u003e   inputs: [...]\n\u003e ]\n\u003e ```\n\n## Getting Started\n\nUse `typed_structor` blocks to define fields with their types:\n\n```elixir\ndefmodule User do\n  use TypedStructor\n\n  typed_structor do\n    field :id, pos_integer(), enforce: true  # Required, never nil\n    field :name, String.t()                  # Optional, nullable\n    field :role, String.t(), default: \"user\" # Has default, not nullable\n  end\nend\n```\n\n### Nullability Rules\n\nThe interaction between `:enforce`, `:default`, and `:null` determines whether a field's type includes `nil`:\n\n| `:default` | `:enforce` | `:null` | Type includes `nil`? |\n|------------|------------|---------|----------------------|\n| `unset`    | `false`    | `true`  | yes                  |\n| `unset`    | `false`    | `false` | no                   |\n| `set`      | -          | -       | no                   |\n| -          | `true`     | -       | no                   |\n\nYou can set `:null` at the block level to change the default for all fields:\n\n```elixir\ntyped_structor null: false do\n  field :id, integer()                         # Not nullable\n  field :email, String.t()                     # Not nullable\n  field :phone, String.t(), null: true         # Override: nullable\nend\n```\n\n## Options\n\n### Opaque Types\n\nUse `type_kind: :opaque` to hide implementation details:\n\n```elixir\ntyped_structor type_kind: :opaque do\n  field :secret, String.t()\nend\n# Generates: @opaque t() :: %__MODULE__{...}\n```\n\n### Custom Type Names\n\nOverride the default `t()` type name:\n\n```elixir\ntyped_structor type_name: :user_data do\n  field :id, pos_integer()\nend\n# Generates: @type user_data() :: %__MODULE__{...}\n```\n\n### Type Parameters\n\nCreate generic types with `parameter/1`:\n\n```elixir\ntyped_structor do\n  parameter :value_type\n  parameter :error_type\n\n  field :value, value_type\n  field :error, error_type\nend\n# Generates: @type t(value_type, error_type) :: %__MODULE__{...}\n```\n\n### Nested Modules\n\nDefine structs in submodules:\n\n```elixir\ndefmodule User do\n  use TypedStructor\n\n  typed_structor module: Profile do\n    field :email, String.t(), enforce: true\n    field :bio, String.t()\n  end\nend\n# Creates User.Profile with its own struct and type\n```\n\n## Plugins\n\nExtend TypedStructor's behavior with plugins that run at compile time:\n\n```elixir\ntyped_structor do\n  plugin Guides.Plugins.Accessible\n\n  field :id, pos_integer()\n  field :name, String.t()\nend\n```\n\nSee the [Plugin Guides](https://hexdocs.pm/typed_structor/introduction.html) for examples and instructions on writing your own.\n\n## Documentation\n\nAdd `@typedoc` inside the block, and `@moduledoc` at the module level as usual:\n\n```elixir\ndefmodule User do\n  @moduledoc \"User account data\"\n  use TypedStructor\n\n  typed_structor do\n    @typedoc \"A user with authentication details\"\n\n    field :id, pos_integer()\n    field :name, String.t()\n  end\nend\n```\n\n\u003c!-- MODULEDOC --\u003e\n\n## Advanced Usage\n\n### Exceptions\n\nDefine typed exceptions with automatic `__exception__` handling:\n\n```elixir\ndefmodule HTTPException do\n  use TypedStructor\n\n  typed_structor definer: :defexception, enforce: true do\n    field :status, non_neg_integer()\n    field :message, String.t()\n  end\n\n  @impl Exception\n  def message(%__MODULE__{status: status, message: msg}) do\n    \"HTTP #{status}: #{msg}\"\n  end\nend\n```\n\n### Records\n\nCreate Erlang-compatible records:\n\n```elixir\ndefmodule UserRecord do\n  use TypedStructor\n\n  typed_structor definer: :defrecord, record_name: :user do\n    field :name, String.t(), enforce: true\n    field :age, pos_integer(), enforce: true\n  end\nend\n```\n\n### Integration with Other Libraries\n\nUse `define_struct: false` to skip struct generation when another library defines the struct:\n\n```elixir\ndefmodule User do\n  use TypedStructor\n\n  typed_structor define_struct: false do\n    field :email, String.t(), enforce: true\n\n    use Ecto.Schema\n    @primary_key false\n\n    schema \"users\" do\n      Ecto.Schema.field(:email, :string)\n    end\n  end\nend\n```\n\nThis generates only the type spec while letting the other library handle the struct definition.\n\nFor full Ecto integration with typed fields, see [EctoTypedSchema](https://github.com/elixir-typed-structor/ecto_typed_schema) -- a companion library built on TypedStructor.\n\n## Learn More\n\n- [HexDocs](https://hexdocs.pm/typed_structor) -- full API reference and guides\n- [Plugin Guides](https://hexdocs.pm/typed_structor/introduction.html) -- build and use plugins\n- [Changelog](https://hexdocs.pm/typed_structor/changelog.html) -- release history\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-typed-structor%2Ftyped_structor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felixir-typed-structor%2Ftyped_structor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-typed-structor%2Ftyped_structor/lists"}