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
|
# 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 Helpers
# TODO: T::Boolean
# TODO: T.nilable
# TODO: T.type_alias
# TODO: T.reveal_type
end
class TheFunPart
end
class TrustMe
# TODO: T.untyped
# TODO: T.cast
# TODO: T.unsafe
# TODO: T.must
# TODO: T.assert_type!
end
class Experimental
# These are not listed in the documentation but are still available for use.
end
class Generics
# TODO
end
|