Sorbet Cheatsheet Part 1
Published on
Note: This was an incomplete draft. You probably want the finished version!
As I start using Sorbet more, I've realized that I need a quick reference for some common patterns. I've always been fond of Learn X in Y minutes, so I'm going to try to copy that style. It'll take a few tries to get this right, so here's the first.
# Every file should have a "typed sigil" that tells Sorbet how strict to be
# during static type checking.
#
# Strictness levels (lax to strict):
#
# ignore: Sorbet won't even read the file. This means its contents are not
# visible during type checking. Avoid this.
#
# false: Sorbet will only report errors related to constant resolution. This
# is the default if no sigil is included.
#
# true: Sorbet will report all static type errors. This is the sweet spot of
# safety for effort.
#
# strict: Sorbet will require that all methods, constants, and instance
# variables have static types.
#
# strong: Sorbet will no longer allow anything to be T.untyped, even
# explicitly. Almost nothing satisfies this.
# typed: true
# Include the runtime type-checking library. This lets you write inline sigs
# and have them checked at runtime (instead of running Sorbet as RBI-only).
# These runtime checks happen even for files with `ignore` or `false` sigils.
# Bring in the type definition helpers. You'll almost always need this.
extend T::Sig
# Sigs are defined with `sig` and a block. Define the return value type with
# `returns`.
#
# This method returns a value whose class is `String`. These are the most
# common types, and Sorbet calls them "class types".
sig { returns(String) }
end
# Define parameter value types with `params`.
sig { params(n: Integer).returns(String) }
(1..n).map { greet }.join()
end
# Define keyword parameters the same way.
sig { params(n: Integer, sep: String).returns(String) }
(1..n).map { greet }.join(sep)
end
# Notice that positional/keyword and required/optional make no difference
# here. They're all defined the same way in `params`.
# For lots of parameters, it's nicer to use do..end and a multiline block
# instead of curly braces.
sig do
params(
str: String,
num: Integer,
sym: Symbol,
).returns(String)
end
end
# For a method whose return value is useless, use `void`.
sig { params(name: String).void }
puts
end
# Splats! Also known as "rest parameters", "*args", "**kwargs", and others.
#
# Type the value that a _member_ of `args` or `kwargs` will have, not `args`
# or `kwargs` itself.
sig { params( args: Integer, kwargs: String).void }
if kwargs[:op] ==
args.each { puts(i - 1) }
else
args.each { puts(i + 1) }
end
end
# Most initializers should be `void`.
sig { params(name: String).void }
# Instance variables must have annotated types to participate in static
# type checking.
# The value in `T.let` is checked statically and at runtime.
@upname = T.let(name.upcase, String)
# Sorbet can infer this one!
@name = name
end
# Constants also need annotated types.
= T.let(, String)
# Class variables too.
@@the_answer = T.let(42, Integer)
end
# TODO: T::Boolean
# TODO: T.nilable
# TODO: T.type_alias
# TODO: T.reveal_type
end
end
# TODO: T.untyped
# TODO: T.cast
# TODO: T.unsafe
# TODO: T.must
# TODO: T.assert_type!
end
# These are not listed in the documentation but are still available for use.
end
# TODO
end