Sorbet Runtime Types

Operandi supports Sorbet runtime types for type validation of arguments and outputs. This provides runtime type checking using Sorbet's type system.

This page covers runtime type checking with sorbet-runtime. For static type analysis with Sorbet and RBI file generation, see Tapioca / Sorbet Integration.

Installation

Add sorbet-runtime to your Gemfile:

gem "sorbet-runtime"

Then run:

bundle install

Basic Usage

When sorbet-runtime is loaded, plain Ruby classes are automatically validated using Sorbet's type system:

require "sorbet-runtime"

class User::Create < ApplicationService
  # Basic types - plain Ruby classes work directly!
  arg :name, type: String
  arg :age, type: Integer
  
  # Nilable types (allows nil)
  arg :email, type: T.nilable(String), optional: true
  
  # Union types (multiple allowed types)
  arg :status, type: T.any(String, Symbol), default: "pending"
  
  # Typed arrays
  arg :tags, type: T::Array[String], optional: true
  
  # Boolean type
  arg :active, type: T::Boolean, default: true

  # Outputs with Sorbet types - plain classes work here too
  output :user, type: User
  output :metadata, type: Hash
  
  step :create_user
  step :build_metadata

  private

  def create_user
    self.user = User.create!(
      name: name,
      age: age,
      email: email,
      status: status,
      tags: tags || [],
      active: active
    )
  end

  def build_metadata
    self.metadata = { created_at: Time.current }
  end
end

Type Reference

Basic Types

When sorbet-runtime is loaded, plain Ruby classes are automatically coerced to Sorbet types:

You can also use T::Utils.coerce(String) explicitly, but it's not required - Operandi handles the coercion automatically.

Nilable Types

Allow nil values with T.nilable:

Union Types

Allow multiple types with T.any:

Typed Arrays

Validate array element types with T::Array:

Generic Type Erasure: Sorbet's T::Array[String] only validates that the value is an Array at runtime. The type parameter (String) is erased and not checked at runtime. For strict element validation, implement custom validation in your service steps.

Boolean Type

Use T::Boolean for true/false values:

Complex Types

Combine types for more complex validations:

Important: Sorbet Types Validate Only

Validation Behavior

If you need coercion, handle it explicitly in your service:

Combining with Tapioca

For full Sorbet support, you can use both:

  1. Runtime types (sorbet-runtime) - Validates types at runtime

  2. Static types (Tapioca) - Generates RBI files for static analysis

See Tapioca / Sorbet Integration for setting up static type analysis.

With Tapioca configured, you get:

  • Runtime validation from sorbet-runtime

  • IDE autocompletion from generated RBI files

  • Static type checking from srb tc

Error Messages

When type validation fails, Operandi raises ArgTypeError with a descriptive message:

Full Example

Last updated