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:ドキュメントには記載がない