Typelizer
Typelizer is a Ruby gem that automatically generates TypeScript interfaces from your Ruby serializers, bridging the gap between your Ruby backend and TypeScript frontend. It supports multiple serializer libraries and provides a flexible configuration system, making it easier to maintain type consistency across your full-stack application.
Table of Contents
Features
- Automatic TypeScript interface generation
- Support for multiple serializer libraries (
Alba
, ActiveModel::Serializer
, Oj::Serializer
) - File watching and automatic regeneration in development
Installation
To install Typelizer, add the following line to your Gemfile
and run bundle install
:
gem "typelizer"
Usage
Basic Setup
Include the Typelizer DSL in your serializers:
class ApplicationResource
include Alba::Resource
include Typelizer::DSL
end
class PostResource < ApplicationResource
attributes :id, :title, :body
has_one :author, serializer: AuthorResource
end
class AuthorResource < ApplicationResource
typelize_from User
attributes :id, :name
end
Typelizer will automatically generate TypeScript interfaces based on your serializer definitions using information from your models.
Manual Typing
You can manually specify TypeScript types in your serializers:
class PostResource < ApplicationResource
attributes :id, :title, :body, :published_at
typelize "string"
attribute :author_name do |post|
post.author.name
end
typelize :string, nullable: true, comment: "Author's avatar URL"
attribute :avatar do
"https://example.com/avatar.png" if active?
end
end
typelize
can be used with a Hash to specify multiple types at once.
class PostResource < ApplicationResource
attributes :id, :title, :body, :published_at
attribute :author_name do |post|
post.author.name
end
typelize author_name: :string, published_at: :string
end
You can also specify more complex type definitions using a lower-level API:
typelize attribute_name: ["string", "Date", optional: true, nullable: true, multi: true, enum: %w[foo bar], comment: "Attribute description"]
TypeScript Integration
Typelizer generates TypeScript interfaces in the specified output directory:
export interface Post {
id: number;
title: string;
category?: "news" | "article" | "blog" | null;
body: string;
published_at: string | null;
author_name: string;
}
All generated interfaces are automatically imported in a single file:
export * from "./post";
export * from "./author";
We recommend importing this file in a central location:
import "@/types/serializers";
With such a setup, you can import all generated interfaces in your TypeScript files:
import { Post } from "@/types";
This setup also allows you to use custom types in your serializers:
class PostWithMetaResource < ApplicationResource
attributes :id, :title
typelize "PostMeta"
attribute :meta do |post|
{ likes: post.likes, comments: post.comments }
end
end
import { PostMeta } from "@/types";
export interface Post {
id: number;
title: string;
meta: PostMeta;
}
The "@/types"
import path is configurable:
Typelizer.configure do |config|
config.types_import_path = "@/types";
end
See the Configuration section for more options.
Manual Generation
To manually generate TypeScript interfaces use one of the following commands:
rails typelizer:generate
rails typelizer:generate:refresh
Automatic Generation in Development
When Listen is installed, Typelizer automatically watches for changes and regenerates interfaces in development mode. You can disable this behavior:
Typelizer.listen = false
Disabling Typelizer
Sometimes we want to use Typelizer only with manual generation. To disable Typelizer during development, we can set DISABLE_TYPELIZER
environment variable to true
. This doesn't affect manual generation.
Configuration
Global Configuration
Typelizer provides several global configuration options:
Typelizer.dirs = [Rails.root.join("app", "resources"), Rails.root.join("app", "serializers")]
Typelizer.reject_class = ->(serializer:) { false }
Typelizer.logger = Logger.new($stdout, level: :info)
Typelizer.listen = nil
Config Options
Typelizer::Config
offers fine-grained control over the gem's behavior. Here's a list of available options:
Typelizer.configure do |config|
config.serializer_name_mapper = ->(serializer) { ... }
config.serializer_model_mapper = ->(serializer) { ... }
config.properties_transformer = ->(properties) { ... }
config.model_plugin = Typelizer::ModelPlugins::Auto
config.serializer_plugin = Typelizer::SerializerPlugins::Auto
config.plugin_configs = { alba: { ts_mapper: {...} } }
config.type_mapping = config.type_mapping.merge(jsonb: "Record<string, undefined>", ... )
config.null_strategy = :nullable
config.output_dir = Rails.root.join("app/javascript/types/serializers")
config.types_import_path = "@/types"
config.types_global << %w[Array Date Record File FileList]
config.verbatim_module_syntax = false
config.comments = false
end
Per-Serializer Configuration
You can also configure Typelizer on a per-serializer basis:
class PostResource < ApplicationResource
typelizer_config do |config|
config.type_mapping = config.type_mapping.merge(jsonb: "Record<string, undefined>", ... )
config.null_strategy = :nullable
end
end
Credits
Typelizer is inspired by types_from_serializers.
License
The gem is available as open source under the terms of the MIT License.