
Ruby の 静的な型解析に関連する RBS や TypeProf 、 Steep を試してみた
はじめに
最近は業務で Ruby (Ruby on Rails)を書いています。
たまに型解析があると嬉しいなと思っています。
Ruby 3 から標準 Gem となった RBS や TypeProf はこれまで触れることがなかったので色々試してみました。
利用した各バージョンは以下の通りです。
$ ruby -v
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-darwin24]
$ rbs -v
rbs 3.8.0
$ typeprof --version
typeprof 0.30.1
$ steep --version
1.9.3
RBS
Ruby プログラムの型情報を記述できるようになります。
これはあくまで型情報の記述ができるだけで、これらを用いて各種の解析ツールがチェックをしてくれます。
README にあるサンプルコードの通り Person クラスとその型情報である rbs ファイルを作成しました。
私は普段 RubyMine を利用しているのですが、これだけで認識をしてくれました。
これがあると助かる場面が多そうですね。

VS Code や Vim のプラグインもドキュメントで紹介をされています。
rbs collection
RBS のバンドラーのような仕組みで、以下のコマンドを実行すると rbs_collection.yaml が生成されます。
$ rbs collection init
生成されたファイルは rbs のドキュメントにも記載されている通りです。
Gemfile のように取得先を指定して、そこからライブラリに紐づく rbs ファイルをインストールしてくれます。
以下のサイトに代表的な Gem は載っていました。
Gemfile を作成した上で、以下のコマンドを実行するとインストール先にインストールされます。
特定の Gem だけ除外することも可能です。
$ rbs collection install
TypeProf
Ruby のコードを静的に解析し、型情報を推測するツールです。
RubyKaigi 2024 の発表資料もありました。
Ruby 3.4 でデフォルトパーサーになった Prism も利用されているようです。
例えば以下のようなクラスを書いて、 typeprof コマンドを実行すると RBS の型情報が生成されました。
class Item
TAX = 1.1
SALE_DESCRIPTION = "Just for Today!"
attr_reader :name
attr_reader :price
def initialize(name:, price:)
@name = name
@price = price
end
def price_with_tax
price * TAX
end
def sale_description
SALE_DESCRIPTION
end
end
$ typeprof item.rb -o item.rbs
# TypeProf 0.30.1
# item.rb
class Item
Item::TAX: Float
Item::SALE_DESCRIPTION: String
def name: -> untyped
def price: -> untyped
def initialize: (name: untyped, price: untyped) -> untyped
def price_with_tax: -> untyped
def sale_description: -> String
end
定数から型情報もちゃんと推測できています。
また以下のようなサンプルコードを渡すと引数などの情報から推測してくれるようです。
上記の型だと初期化時の name や price パラメータが untyped でしたが、String や Integer で返るようになります。
item = Item.new(name: 'banana', price: 100)
item.price_with_tax
class Item
Item::TAX: Float
Item::SALE_DESCRIPTION: String
def name: -> String
def price: -> Integer
def initialize: (name: String, price: Integer) -> Integer
def price_with_tax: -> Float
def sale_description: -> String
end
ただ、先ほどの RBS で他のライブラリの型情報をローカルにインストールすると、以下のエラーになることがありそうです(私の場合は faraday でした)
ちゃんと調べていないのですが、まだ typeprof 側で対応してないこともあるんでしょうか。
この場合は対象のライブラリの RBS データを一度削除する必要があります。
unsupported: RBS::AST::Members::InstanceVariable (RuntimeError)
余談ですが、rbs collection 経由でインストールした場合は、lock ファイルに記載が残っているとローカルから手動削除するだけではダメなので、ちゃんと rbs_collection.yaml 側も修正する必要があります。
typeprof をざっとプロジェクトクラスに適用して、RBS の初期情報を生成もできそうですが、基本的には私のような一般開発者はそこまで利用せずに後述する静的解析ツールや各種エディターの拡張を通して利用することになりそうです。
Steep
Steep は、RBS を使用して Ruby コードの型検査を行うツールです。Steep は、RBS で定義された型情報に基づいてコードを解析し、型エラーを検出することができます。
ちなみに Sorbet というツールもあるようですが、こちらは RBI と呼ばれる独自の型アノテーション形式を使用するようです。
steep init した後に生成される Steepfile をみるといくつかオプションがあるようです。
モジュールごとに分けて検査することも可能そうです。
ガイドがあったのでそちらの通りにやってみました。
String 型の引数に対して、わざと nil を入れてみるとちゃんと検出されました。
phone = Phone.new(country: nil, number: nil)
pp phone.country
[error] Cannot pass a value of type `nil` as an argument of type `::String`
│ nil <: ::String
│
│ Diagnostic ID: Ruby::ArgumentTypeMismatch
│
└ phone = Phone.new(country: nil, number: nil)
rbs_rails
じゃあ Rails に実際に導入するにはどうしたら良いかなと思ったら、Rails 関連のファイルの RBS を生成してくれるライブラリがありました。
とりあえず簡単な Rails プロジェクトを作って入れてみることに。
余談ですが、 rails new して 8 系がインストールされてワクワクしますねw
8 系はまだあんまり調べてないので、今度改めて調査したいと思います。
(個人的には Solid 系のアダプターが面白そうだなとは思いました)
rbs_rails:install タスクを実行すると、lib/tasks/rbs.rake ファイルができます。
主な実態は lib/rbs_rails/rbs_task.rb にあるようで、モデルやヘルパーなどの定義を生成するコマンドを提供してくれています。
モデルの方の自動生成で対応しているのは ActiveRecord のクラスのようです。
基本的にはデータベースを参照し、カラムに応じた ActiveRecord が生やしたメソッドの定義を作ってくれます。
その後、 rbs collection や steep を利用していきます。
感想
静的な型解析とはいえ、テストの補助として利用することで抜け漏れの防止にもつながりそうだなと思います。
個人的には、やはりエディターや IDE の補完が強くなるのが開発体験の向上にもつながるのではないかと考えます。
一方で、 rbs_rails などを利用したとしても、自分(チーム)で RBS ファイルを整備しないといけない手間は依然として残ります。
できる範囲から始めるためにも Steep の設定で、例えば app/models から始める、とかが良さそうですかね。