Sorbet syntax

From wikinotes

The docs for sorbet are really, really good.
I'd probably just go there.

Basics

# person.rb

class Person
  def create(name:, age:, object:); end
  def speak; "foo"; end
end

Common Cases

attr_reader/writer/accessor

sig { returns(String) }
attr_reader :name

include module

require_ancestors https://sorbet.org/docs/requires-ancestor
mix_in_class_methods https://sorbet.org/docs/abstract

instance methods should just work

module AnimalMethods
  sig { returns(T.void) }
  def walk(); end
end

class Cat
  include AnimalMethods
end

class methods

module AnimalMethods
  extend T::Sig
  extend T::Helpers

  # modify to set classmethods on include
  def self.included(base = nil)
    base&.extend(ClassMethods)
  end

  # define classmethods
  module ClassMethods
    sig { returns(String) }
    def speak(); end
  end

  # inform sorbet of classmethods
  mixes_in_class_methods ClassMethods
end

class Cat
  include AnimalMethods
end

rails concerns && included do

procs/lambdas

sig { params(block: T.proc.params(arg0: Integer).void).void }
def foo(&block)

If your proc is designed to be executed in a different context
(for example, proc is set as a class method, but designed to run on a class instance)
you can use T.bind(self, YourClass).

Types

# stdlib types
String
Symbol
Integer
Float
NilClass

# generic types
T::Array[Integer]
T::Set[Integer]
T::Hash[Symbol, String]
T::Enumerable[Integer]
T::Enumerator[Integer]
T::Range[Integer]

# sorbet types
T::Boolean              # true/false
T.untyped               # anything
T.nilable(String)       # Unions: string or nil
T.any(String, Integer)  # Unions: either string or integer
T.noreturn              # if method never returns (exception, exit process, loop forever, ...)

sig { params(...).void }  # ignore return value, not meant to be used

Generics

# a generic method, that takes param of arbitrary type 'T' and returns type 'T'
sig do
  type_parameters(:T)
    .params(var: T.type_parameter(:T))
    .returns(T.type_parameter(:T))
end
def foo(var)
  var
end

Tools

T.type_alias
T.bind(self, YourClass)              # use within proc, to indicate the instance it should be executed in (defaults to current)
T.cast(var, YourClass)
mixes_in_class_methods ClassMethods  # when class included, ClassMethods become static methods on target class
requires_ancestor Kernel             # any class that includes this module, must also include Kernel
T::Utils                             # provided by sorbet-runtime, tools for getting information about sigs, adding sigs, etc.

Tricks

Type Aliases

You can define re-usable type-sigs with type aliases.

class University
  extend T::Sig

  StudentMap = T.type_alias { T::Hash[Symbol, String] }

  sig { params(student_map: StudentMap).returns(Integer) }
  def count_students(student_map)
    student_map.count
  end

  sig { params(student_map: StudentMap).returns(List[String]) }
  def name_students(student_map)
    student_map.keys()
  end
end

Dynamically setting a Type Signature

class Foo
  sig { params(a: Integer).void }
  def bar(a) = nil
end

T::Private::Methods.declare_sig(Foo, nil, :final) { params(a: String).void }
declaration = T::Private::DeclState.current.active_declaration
T::Private::Methods.run_sig(Foo, :bar, Foo.instance_method(:bar), declaration)

# voila! signature installed
T::Utils.signature_for_instance_method(Foo, :bar)