1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
# 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.
require 'sorbet-runtime'
class BasicSigs
# 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) }
def greet
'Hello, World!'
end
# Define parameter value types with `params`.
sig { params(n: Integer).returns(String) }
def greet_repeat(n)
(1..n).map { greet }.join("\n")
end
# Define keyword parameters the same way.
sig { params(n: Integer, sep: String).returns(String) }
def greet_repeat(n, sep: "\n")
(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
def uhh(str:, num:, sym:)
'What would you even do with these?'
end
# For a method whose return value is useless, use `void`.
sig { params(name: String).void }
def say_hello(name)
puts "Hello, #{name}!"
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 }
def no_op(*args, **kwargs)
if kwargs[:op] == 'minus'
args.each { |i| puts(i - 1) }
else
args.each { |i| puts(i + 1) }
end
end
# Most initializers should be `void`.
sig { params(name: String).void }
def initialize(name:)
# 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.
SORBET = T.let('A delicious frozen treat', String)
# Class variables too.
@@the_answer = T.let(42, Integer)
end
class StandardHelpers
# TODO: T::Boolean, T::Array, T::Range, T::Hash, T::Enumerable, ...
# TODO: T.nilable
# TODO: T.class_of
# TODO: T.proc
end
class Combinators
# TODO: T.any
# TODO: T.all
end
class DataClasses
# TODO: T::Struct
# TODO: ^ reference equality, not value equality
# TODO: T::Enum
end
class FlowSensitivity
# TODO: T.absurd
# TODO: T.noreturn
end
class Metaprogramming
# TODO: T.type_alias
# TODO: T.self_type
# TODO: T.attached_class
end
class InheritanceChecks
# TODO: abstract!
# TODO: interface!
# TODO: abstract. / override.
# TODO: mixes_in_class_methods
# TODO: final!
# TODO: sealed!
end
class Debugging
# TODO: T.reveal_type
end
class EscapeHatches
# TODO: T.untyped
# TODO: T.cast
# TODO: T.unsafe
# TODO: T.must
# TODO: T.assert_type!
end
# The following types are not officially documented but are still useful.
class ValueSet
# TODO: T.enum
end
class Generics
# TODO: type_parameters / T.type_parameter
# TODO: T::Generic
# TODO: type_member
end
|