Skip to content

mitchbne/dry-rb-typescript

Repository files navigation

dry-typescript

A Ruby gem that converts Dry::Struct definitions to TypeScript types.

Installation

Add to your Gemfile:

gem 'dry-typescript'

Getting Started in Rails

Setup

Configure in an initializer (config/initializers/dry_typescript.rb):

Dry::TypeScript.configure do |config|
  config.output_dir = Rails.root.join("app/javascript/types")
  config.dirs = [
    Rails.root.join("app/resources"),
    Rails.root.join("app/structs")
  ]
end

Rake Tasks

These rake tasks are automatically available:

  • rails dry_typescript:generate - Generate TypeScript files from all Dry::Struct classes
  • rails dry_typescript:refresh - Clean and regenerate
  • rails dry_typescript:clean - Remove generated files
  • rails dry_typescript:check - Check if types are up to date (exits 1 if stale, for CI)

File Watching (Optional)

Add the listen gem to automatically regenerate TypeScript files when struct files change:

gem 'listen', group: :development

The watcher starts automatically in development when listen is available.

# Force enable/disable listening
Dry::TypeScript.listen = true   # Force enable
Dry::TypeScript.listen = false  # Force disable
Dry::TypeScript.listen = nil    # Auto-detect (default)

# Disable via environment variable
ENV["DISABLE_DRY_TYPESCRIPT"] = "true"

Enable debug output:

DRY_TYPESCRIPT_DEBUG=1 rails server

Getting Started (Non-Rails)

Setup

require 'dry-typescript'

Dry::TypeScript.configure do |config|
  config.output_dir = "lib/types"
end

Rake Tasks

Add to your Rakefile:

require 'dry/typescript/rake_task'

Dry::TypeScript::RakeTask.new(:typescript) do |t|
  t.output_dir = "lib/types"
  t.structs = [User, Address, Order]
end

This provides:

  • rake typescript:generate - Generate TypeScript files
  • rake typescript:clean - Remove generated files

Manual Generation

generator = Dry::TypeScript::Generator.new(structs: [User, Address, Order])
sorted = generator.sorted_structs

writer = Dry::TypeScript::Writer.new
writer.write_all(sorted)
writer.cleanup(current_structs: sorted)

Configuration

Global Configuration

Dry::TypeScript.configure do |config|
  config.output_dir = "app/javascript/types"
  config.export_style = :named    # :named (default) or :default
  config.null_strategy = :optional  # :nullable, :optional, or :nullable_and_optional
  config.barrel_file = true       # false (default) - generate index.ts barrel file
  config.type_name_transformers = [Dry::TypeScript::Transformers.strip_struct_suffix]
  config.property_name_transformer = ->(name) { name.to_s.camelize(:lower) }
end

Export styles:

  • :named (default) - export type User = {...} with barrel export type { User } from './User'
  • :default - type User = {...} + export default User with barrel export { default as User } from './User'

Barrel file:

  • false (default) - No index.ts generated. Direct imports recommended to avoid bundle bloat.
  • true - Generates index.ts with re-exports of all types.

Per-Struct Configuration

Override global settings for individual structs:

class User < Dry::Struct
  extend Dry::TypeScript::StructMethods
  extend Dry::TypeScript::PerStructConfig

  typescript_config do |config|
    config.type_name = "UserResponse"           # Custom TypeScript type name
    config.export_style = :default              # :named or :default
    config.null_strategy = :optional            # :nullable, :optional, or :nullable_and_optional
    config.type_name_transformers = [Dry::TypeScript::Transformers.strip_struct_suffix]
    config.property_name_transformer = ->(name) { name.to_s.camelize(:lower) }
  end

  attribute :name, Types::String
end

Programmatic Usage

class User < Dry::Struct
  extend Dry::TypeScript::StructMethods

  attribute :name, Types::String
  attribute :age, Types::Integer
  attribute :email, Types::String.optional
end

User.to_typescript
# => { typescript: "export type User = {\n  name: string;\n  age: number;\n  email: string | null;\n}", dependencies: [] }

Features

  • Fingerprint-based change detection: Only rewrites files when content changes
  • Import generation: Automatically generates imports for struct dependencies
  • Barrel exports: Optionally creates index.ts with all type exports (disabled by default)
  • Safe cleanup: Only removes files generated by dry-typescript
  • Collision detection: Raises error if multiple structs resolve to same filename
  • Atomic writes: Uses temp files to prevent partial writes
  • Deterministic output: Imports and exports are sorted alphabetically

License

MIT

About

Generate Typescript definitions from DryRB Structs

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Contributors

Languages