Packages

Define once in Elixir. Generate everywhere in TypeScript: serializers, Phoenix route helpers and Inertia page props.

Current section

Files

Jump to
typelizer README.md
Raw

README.md

# Typelizer
**Define once in Elixir. Generate everywhere in TypeScript.**
Typelizer is a library for Phoenix apps with a TypeScript frontend. It gives
you:
- **A serializer DSL** that is both the runtime serializer and the source of
the TypeScript types.
- **TypeScript interfaces inferred from Ecto schemas**. `Ecto.Enum` fields
become literal unions.
- **Typed route helpers** generated from your Phoenix router, with URL defaults
and typed query params.
- **Envelope and pagination helpers** (`Envelope<T>`, `Paginated<T>`) for JSON
APIs and Inertia props.
- **Typed page props for Inertia.js**, with a runtime check in dev and test.
- **`mix typelizer.gen`** to write the files and **`mix typelizer.check`** to
fail on drift in CI and in git hooks.
## Installation
```elixir
def deps do
[
{:typelizer, "~> 0.2"}
]
end
```
`ecto`, `ecto_sql`, `phoenix`, `plug` and `inertia` are optional dependencies.
Typelizer uses them when your app has them. Typelizer supports Elixir 1.15+
and OTP 26+.
## Quick start: Phoenix + Inertia + React
**1. Write a serializer.** It serializes at runtime and it defines the type.
```elixir
defmodule MyAppWeb.TaskSerializer do
use Typelizer.Serializer, schema: MyApp.Tasks.Task
attributes [:id, :title, :status, :due_on]
attribute :overdue, type: :boolean, value: &MyApp.Tasks.overdue?/1
has_one :assignee, serializer: MyAppWeb.UserSerializer, nullable: true
end
```
**2. Declare the props of your Inertia pages** in the controller.
```elixir
defmodule MyAppWeb.TaskController do
use MyAppWeb, :controller
use Typelizer.InertiaPage
page "tasks/index", props: [tasks: {:list, MyAppWeb.TaskSerializer}]
def index(conn, _params) do
conn
|> assign_prop(:tasks, MyAppWeb.TaskSerializer.serialize_many(MyApp.Tasks.list()))
|> render_inertia("tasks/index")
end
end
```
**3. Configure and generate.**
```elixir
# config/config.exs
config :typelizer, repo: MyApp.Repo
config :inertia, camelize_props: true
# config/dev.exs and config/test.exs
config :typelizer, validate_inertia_props: true
```
```elixir
# lib/my_app_web/router.ex, in the :browser pipeline, after Inertia.Plug
plug Typelizer.InertiaPage.ValidateProps
```
```sh
mix typelizer.gen
```
**4. Use the types in React.**
```tsx
import type { TasksIndexProps } from "@/generated/pages";
import { routes } from "@/generated/routes";
export default function Index({ tasks }: TasksIndexProps) {
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>
<a href={routes.task.show(task.id).url}>{task.title}</a>
{task.status === "done" && " ✓"}
</li>
))}
</ul>
);
}
```
**5. Fail on drift.** Run `mix typelizer.check` in a pre-commit hook, in CI and
before a deploy. It exits with status 1 when the committed TypeScript does not
match the Elixir code.
## Guides
- [Getting started](guides/getting-started.md)
- [Serializers](guides/serializers.md)
- [Route helpers](guides/routes.md)
- [Inertia page props](guides/inertia.md)
- [Configuration](guides/configuration.md)
## Comparison with the Ruby gem
Typelizer for Elixir is a clean-room port of the idea behind the Ruby gem
[typelizer](https://github.com/skryukov/typelizer) by Svyatoslav Kryukov.
It shares no code with it.
| | Ruby typelizer | Typelizer for Elixir |
|---|---|---|
| Serializers | Adds types to existing serializer libraries (Alba, ActiveModel::Serializers and others) | Ships its own serializer DSL: one module serializes and defines the type |
| Type source | ActiveRecord models and the database schema | Ecto schemas, plus PostgreSQL column nullability when a repo is configured |
| Enums | Model enums | `Ecto.Enum` → literal unions |
| Route helpers | From `config/routes.rb` | From the Phoenix router |
| Inertia page props | No | Yes, with a runtime check in dev and test |
| Drift check | Your own task | Built in: `mix typelizer.check` |
| Regeneration | A file listener in development | `mix typelizer.gen` |
## Roadmap
Not in v0.1:
- Zod schemas as an output.
- OpenAPI documents as an output.
- Ash resources as a type source.
## Credits
The idea, the name and the developer experience come from
[skryukov/typelizer](https://github.com/skryukov/typelizer). Thank you.
## License
MIT. See [LICENSE](https://github.com/toptive/typelizer-ex/blob/main/LICENSE).