Type Signatures

30.1 RBS (Ruby's Official Type Signature Language)

TypeProf infers types from code. Ruby LSP uses RBS for better completion.

# sig/bank_account.rbs
class BankAccount
  attr_reader owner: String
  attr_reader balance: Integer

  def initialize: (String owner, ?Integer balance) -> void
  def deposit: (Integer amount) -> self
  def withdraw: (Integer amount) -> bool
  def self.accounts_created: () -> Integer
end

interface _Writer
  def <<: (String) -> untyped
end

type user_id = Integer
type result[T] = { ok: true, value: T } | { ok: false, error: String }
gem install rbs steep
rbs prototype rb lib/bank_account.rb > sig/bank_account.rbs   # generate a skeleton
steep init && steep check                                      # type-check with Steep
rbs collection install                                         # signatures for gems

30.2 Sorbet (Stripe's Gradual Type Checker)

Run srb tc for static checks. Sorbet also has runtime checks, Tapioca (generates RBI files for gems), and strictness levels (ignore, false, true, strict, strong). Inline RBS comments (#: (Integer) -> Integer) are increasingly supported by Sorbet and the tooling.

# typed: strict
require "sorbet-runtime"

class Calculator
  extend T::Sig

  sig { params(a: Integer, b: Integer).returns(Integer) }
  def add(a, b) = a + b

  sig { params(values: T::Array[Float]).returns(T.nilable(Float)) }
  def average(values)
    return nil if values.empty?
    values.sum / values.size
  end
end

30.3 When to Use Types

Large codebases, libraries with public APIs, and teams that need refactoring safety. Adopt gradually: types complement tests, they don't replace them.