# `TypedStructor`
[🔗](https://github.com/elixir-typed-structor/typed_structor/blob/v0.6.1/lib/typed_structor.ex#L1)

## Installation

Add `:typed_structor` to your dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:typed_structor, "~> 0.6"}
  ]
end
```

> #### Formatter Setup {: .tip}
>
> Add `:typed_structor` to your `.formatter.exs` for proper indentation:
>
> ```elixir
> [
>   import_deps: [..., :typed_structor],
>   inputs: [...]
> ]
> ```

## Getting Started

Use `typed_structor` blocks to define fields with their types:

```elixir
defmodule User do
  use TypedStructor

  typed_structor do
    field :id, pos_integer(), enforce: true  # Required, never nil
    field :name, String.t()                  # Optional, nullable
    field :role, String.t(), default: "user" # Has default, not nullable
  end
end
```

### Nullability Rules

The interaction between `:enforce`, `:default`, and `:null` determines whether a field's type includes `nil`:

| `:default` | `:enforce` | `:null` | Type includes `nil`? |
|------------|------------|---------|----------------------|
| `unset`    | `false`    | `true`  | yes                  |
| `unset`    | `false`    | `false` | no                   |
| `set`      | -          | -       | no                   |
| -          | `true`     | -       | no                   |

You can set `:null` at the block level to change the default for all fields:

```elixir
typed_structor null: false do
  field :id, integer()                         # Not nullable
  field :email, String.t()                     # Not nullable
  field :phone, String.t(), null: true         # Override: nullable
end
```

## Options

### Opaque Types

Use `type_kind: :opaque` to hide implementation details:

```elixir
typed_structor type_kind: :opaque do
  field :secret, String.t()
end
# Generates: @opaque t() :: %__MODULE__{...}
```

### Custom Type Names

Override the default `t()` type name:

```elixir
typed_structor type_name: :user_data do
  field :id, pos_integer()
end
# Generates: @type user_data() :: %__MODULE__{...}
```

### Type Parameters

Create generic types with `parameter/1`:

```elixir
typed_structor do
  parameter :value_type
  parameter :error_type

  field :value, value_type
  field :error, error_type
end
# Generates: @type t(value_type, error_type) :: %__MODULE__{...}
```

### Nested Modules

Define structs in submodules:

```elixir
defmodule User do
  use TypedStructor

  typed_structor module: Profile do
    field :email, String.t(), enforce: true
    field :bio, String.t()
  end
end
# Creates User.Profile with its own struct and type
```

## Plugins

Extend TypedStructor's behavior with plugins that run at compile time:

```elixir
typed_structor do
  plugin Guides.Plugins.Accessible

  field :id, pos_integer()
  field :name, String.t()
end
```

See the [Plugin Guides](https://hexdocs.pm/typed_structor/introduction.html) for examples and instructions on writing your own.

## Documentation

Add `@typedoc` inside the block, and `@moduledoc` at the module level as usual:

```elixir
defmodule User do
  @moduledoc "User account data"
  use TypedStructor

  typed_structor do
    @typedoc "A user with authentication details"

    field :id, pos_integer()
    field :name, String.t()
  end
end
```

# `field`
*macro* 

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 to `true`, enforces the field, and makes its type
    non-nullable if `:default` is not set
  * `:null` - when set to `true` (the default), allows `nil` to be added to the
    field type when `:default` is not set and `:enforce` is not set; when set
    to `false`, prevents `nil` from being added in that case

> ### How `:default`, `:enforce` and `:null` affect `type` and `@enforce_keys` {: .tip}

> | **`:default`** | **`:enforce`** | **`:null`** | **`type`**        | **`@enforce_keys`** |
> | -------------- | -------------- | ----------- | ----------------- | ------------------- |
> | `set`          | -              | -           | `t()`             | `excluded`          |
> | `unset`        | `true`         | -           | `t()`             | **`included`**      |
> | `unset`        | `false`        | `true`      | **`t() \| nil`** | `excluded`          |
> | `unset`        | `false`        | `false`     | `t()`             | `excluded`          |

# `parameter`
*macro* 

Defines a type parameter in a `typed_structor/2`.

## Example

    # A type parameter named int
    parameter :int

    field :number, int # not int()

# `plugin`
*macro* 

Registers a plugin for the currently defined struct.

## Example

    typed_structor do
      plugin MyPlugin

      field :string, String.t()
    end

For 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.

# `typed_structor`
*macro* 

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` - if `true`, the struct will enforce the keys, see `field/3` options for more information.
  * `:null` - if `true` (default), fields without a default value and not enforced will have `nil` added to their type. If `false`, prevents `nil` from being added.
  * `:definer` - the definer module to use to define the struct, record or exception. Defaults to `:defstruct`. It also accepts a macro that receives the definition struct and returns the AST. See definer section below.
  * `:type_kind` - the kind of type to use for the struct. Defaults to `type`, can be `opaque` or `typep`.
  * `:type_name` - the name of the type to use for the struct. Defaults to `t`.

## Definer
The available definers are:
- `:defstruct`, which defines a struct and a type for a given definition
- `:defexception`, which defines an exception and a type for a given definition
- `:defrecord`, which defines record macros and a type for a given definition
- `:defrecordp`, which defines private record macros and a type for a given definition

### `:defstruct` options

* `:define_struct` - if `false`, the type will be defined, but the struct will not be defined. Defaults to `true`.

### `:defexception` options

* `:define_struct` - if `false`, the type will be defined, but the struct will not be defined. Defaults to `true`.

### `:defrecord` and `:defrecordp` options

* `:record_name`(**required**) - the name of the record, it must be provided.
* `:record_tag` - if set, the record will be tagged with the given value. Defaults to `nil`.
* `:define_record` - if `false`, the type will be defined, but the record will not be defined. Defaults to `true`.

### custom definer

    defmodule MyStruct do
      use TypedStructor

      typed_structor definer: MyDefiner do

        field :name, String.t()
        field :age, integer()
      end
    end

## Examples

    defmodule MyStruct do
      use TypedStructor

      typed_structor do
        field :name, String.t()
        field :age, integer()
      end
    end

Creates the struct in a submodule instead:

    defmodule MyStruct do
      use TypedStructor

      typed_structor module: Struct do
        field :name, String.t()
        field :age, integer()
      end
    end

To 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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
