saleyn

saleyn

TypedStruct 0.6.2: Added Ecto Integration, Default Plugins, and Enhanced Struct Features

I wanted to share some exciting updates to TypedStruct that have landed recently, bringing significant improvements to struct definition ergonomics, database integration, and runtime utilities.

What’s New

1. Validated Constructor Generation

Generate automatic constructors with validation for your structs. Enable with constructors: true:

defmodule User do
  use TypedStruct

  typedstruct constructors: true do
    field :name, String.t(), enforce: true
    field :age, integer()
  end
end

# Generated constructors:

User.new(name: "Alice", age: 30)       # Safe creation with validation
User.new!(name: "Alice")               # Raises on invalid data
User.from_map(%{"name" => "Alice"})    # From map
User.from_keyword(name: "Alice")       # From keyword list

# For typedrecord:

# user_new/1, user_new!/1, user_from_map/1, user_from_keyword/1

2. Reflection Helpers

Get runtime reflection information about your structs with reflection: true:

defmodule User do
  use TypedStruct

  typedstruct reflection: true do
    field :id, integer(), enforce: true
    field :name, String.t(), enforce: true
    field :email, String.t()
    field :active, boolean(), default: true
  end
end

# Generated helpers:

User.__fields__()              # [:id, :name, :email, :active]
User.__required__()            # [:id, :name]
User.__defaults__()            # %{email: nil, active: true}
User.__types__()               # Internal type info for tooling

# For typedrecord named :user:

# user_fields/0, user_required/0, user_defaults/0, user_types/0

3. Dynamic Defaults with default_fn

Use dynamic defaults generated at construction time:

defmodule Token do
  use TypedStruct

  defmodule Defaults do
    def token, do: :crypto.strong_rand_bytes(32) |> Base.encode16()
    def timestamp, do: System.os_time(:second)
  end

  typedstruct do
    field :value, binary(), default_fn: &Defaults.token/0
    field :created_at, integer(), default_fn: &Defaults.timestamp/0
  end
end

# Each new token gets a fresh random value and timestamp

4. Strict Mode

Enforce all fields to either be enforced or have defaults:

defmodule StrictUser do
  use TypedStruct

  typedstruct strict: true do
    field :id, integer(), enforce: true
    field :name, String.t(), enforce: true
    field :active, boolean(), default: true
    # field :email, String.t()  # Error: must have enforce or default!
  end
end

iex(2)> %StrictUser{}
** (ArgumentError) the following keys must also be given when building struct StrictUser: [:name, :id]
    expanding struct: StrictUser.__struct__/1

5. Protocol Derivation Passthrough

Pass through derive options to defstruct:

defmodule User do
  use TypedStruct
  typedstruct derive: [Inspect, Poison.Encoder] do
    field :id, integer()
    field :name, String.t()
  end
end

6. Default Plugins for Wrapper Macros

Specify default plugins when creating TypedStruct wrapper modules:


defmodule MyApp.Type do
  defmacro __using__(_) do
    quote do
      use TypedStruct, plugins: [MyApp.TypedStruct.Plugin.Validators]
    end
  end
end

defmodule MyApp.User do
  use MyApp.Type  # Automatically gets Validators plugin

  typedstruct do
    field :name, String.t(), enforce: true
  end
end

7. Ecto Schema Integration

Define Ecto schemas with automatic type inference in a single, unified syntax:

defmodule User do
  use TypedStruct.Ecto

  typedschema "users" do
    field :email, :string, enforce: true
    field :age, :integer
    belongs_to :company, Company
    has_many :posts, Post
    timestamps()
  end
end

The typedschema macro:

  • Automatically infers Elixir types from Ecto field types (20+ type mappings)
  • Generates both Ecto schema and TypedStruct with matching types
  • Handles all association types: belongs_to, has_many, has_one, many_to_many
  • Supports regular and polymorphic embeds
  • Enables type overrides with :: syntax

8. Polymorphic Embed Support

When you have the polymorphic_embed library installed, TypedStruct.Ecto automatically generates proper union types:

defmodule Notification do
  use TypedStruct.Ecto

  typedschema "notifications" do
    field :message, :string, enforce: true
    polymorphic_embeds_one :channel,
      types: [
        email: EmailConfig,
        sms: SMSConfig,
        push: PushConfig
      ],

    on_replace: :update
    timestamps()
  end
end

9. Field Documentation via @doc Attribute

Document fields using the @doc attribute directly in your struct:

typedstruct do
  @doc "User's full name"
  field :name, String.t()
  @doc "Email address for notifications"
  field :email, String.t(), enforce: true
end

Why This Matters

These features address common pain points in Elixir development:

  • Less Boilerplate — Generate constructors and reflection helpers automatically
  • Type Safety — Static types with runtime constructor validation
  • Optional Database Integration — Keep schemas and types in sync with TypedStruct.Ecto
  • Runtime Reflection — Inspect struct metadata programmatically
  • Flexible Defaults — Support both static and dynamic default values
  • Protocol Support — Derive protocols like Inspect and Jason.Encoder

Feel free to open issues or discussions on the GitHub repository.

Showing Posts 1 to 1

saleyn

saleyn OP

Also added support for defining Erlang records with type information and their pretty-printing:

iex(1)> defmodule Person do
  use TypedStruct

  typedrecord :person do
    @typedoc "A person"

    field :name, String.t()
    field :age,  non_neg_integer(), default: 0
  end
end

iex(2)> require Person    # <-- This is essential!
Person
iex(3)> Person.person()
Person.person(name: nil, age: 0)
iex(4)> rec = Person.person(name: "John")
Person.person(name: "John", age: 0)
iex(5)> {:person, "John", 0}
Person.person(name: "John", age: 0)
— All posts loaded —

Where Next? Top

Trending in News & Updates Top

mat-hek
Popcorn, the library that runs Elixir code in the browser, is now stable :popcorn: This is possible because it now builds on a very stab...
New
danj
Some root certs are compromised, updates are needed to maintain TLS security. As reported by Mozilla the eMudhra intermediate CA privat...
New
mandar1045
Stampede 1.2 is out, and I finally got to try it at a bigger scale. What’s new --find-limit: instead of a fixed number of users, it add...
New
bartblast
I talk to a lot of people in the Elixir community, and most of them already know about Hologram. But before they give it a try they want ...
New
mikehostetler
ReqLLM v1.29.0 has been released. Security update ReqLLM 1.29.0 fixes GHSA-6vww-5m28-f26v. Versions 1.9.0 through 1.28.0 did not verify ...
New
rodloboz
Hi all, I’ve just released Sake 0.5.0, and I want to explain better what it’s for, because I think many Phoenix apps can use it now. Wh...
New
uzaydurdu
Hey Elixir folks, I’ve been working on Xenia 0.2.0, and it’s nearly ready. This release focuses on performance, easier integration, and...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
raa-ziq
Hi everyone :waving_hand: I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
kip
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews