SteepのAnnotationに関する備忘録 #asakusa_bashi_rbs
Steepのmanual/annotations.mdを読みながら、実際にコードを書いて覚えたことをブログにまとめる。
変数
変数の型を String?
から String
にするときに便利そう。
# @type var value: String value = %w[a b c].sample puts('Hi, ' + value)
アノテーションがない場合、 sample: () -> String?
なので型検査エラーになる。
app/user.rb:2:14: [error] Cannot pass a value of type `(::String | nil)` as an argument of type `::string` │ (::String | nil) <: ::string │ (::String | nil) <: (::String | ::_ToStr) │ nil <: (::String | ::_ToStr) │ nil <: ::String │ │ Diagnostic ID: Ruby::ArgumentTypeMismatch │ └ puts('Hi, ' + value) ~~~~~ Detected 1 problem from 1 file
インスタンス変数
変数と同じく、インスタンス変数の型を変えるときに便利そう。
# ruby class User def initialize(name) @name = name end def hi # @type ivar @name: String puts('Hi, ' + @name) end def bye puts('bye, ' + @name) end end # rbs class User @name: String? def initialize: (String?) -> void def hi: () -> void def bye: () -> void end
これは bye
の方だけ型検査エラーになる。
app/user.rb:12:19: [error] Cannot pass a value of type `(::String | nil)` as an argument of type `::string` │ (::String | nil) <: ::string │ (::String | nil) <: (::String | ::_ToStr) │ nil <: (::String | ::_ToStr) │ nil <: ::String │ │ Diagnostic ID: Ruby::ArgumentTypeMismatch │ └ puts('bye, ' + @name) ~~~~~ Detected 1 problem from 1 file
クラスとメソッド
Rubyのコード内にアノテーションを書くと、RBSなしでもSteepで型チェックできる。
# @type const User : User class User # @type method say: (String, Integer) -> void def say(name, age) puts(name + age) # 型検査エラー end end User.new.say(1, 2) # ここは型エラーにならない
このコードをSteepで検査すると、 say
メソッドの中だけ型検査の対象になっていることがわかる。
app/user.rb:5:16: [error] Cannot pass a value of type `::Integer` as an argument of type `::string` │ ::Integer <: ::string │ ::Integer <: (::String | ::_ToStr) │ ::Integer <: ::String │ ::Numeric <: ::String │ ::Object <: ::String │ ::BasicObject <: ::String │ │ Diagnostic ID: Ruby::ArgumentTypeMismatch │ └ puts(name + age) # 型検査エラー ~~~ Detected 1 problem from 1 file
pure
メソッドに副作用がないことを明示するときに指定する。*1
# ruby class User def initialize(name) @name = name end def name @name end end user = User.new(nil) if user.name puts("Hi, " + user.name) end # rbs class User def initialize: (String?) -> void %a{pure} def name: () -> String? end
%a{pure}
を指定しない場合、以下のような型検査エラーが起きる。
app/user.rb:13:16: [error] Cannot pass a value of type `(::String | nil)` as an argument of type `::string` │ (::String | nil) <: ::string │ (::String | nil) <: (::String | ::_ToStr) │ nil <: (::String | ::_ToStr) │ nil <: ::String │ │ Diagnostic ID: Ruby::ArgumentTypeMismatch │ └ puts("Hi, " + user.name) ~~~~~~~~~ Detected 1 problem from 1 file
*1:ドキュメントには記載がない