RBSでRubyに型を付けよう!typeprofとsteepで静的型チェック入門
生徒
「先生、Rubyって“動的型付け”の言語って聞きましたけど、型っていらないんですか?」
先生
「とても良い質問です。Rubyは型を自動で判断してくれるので“型を宣言する必要がない”のが特徴なんです。ただし、大きなプログラムになると『この変数はどんな型?』って分かりにくくなることがあります。」
生徒
「なるほど…。じゃあ、型を明示できたら便利そうですね!」
先生
「その通り!そこで登場するのがRBS(Ruby Signature)やtypeprof、そしてsteepという仕組みなんです。今日はそれを一緒に学んでいきましょう!」
1. RBSとは?Rubyに“型の設計図”を与える仕組み
Rubyはとても自由な言語で、変数にあとから別の型の値を代入してもエラーになりません。これを動的型付けといいます。最初はとても書きやすく感じますが、ファイル数やクラスが増えてくると、「ここには本当はどんな値が入る想定なんだろう?」と迷子になりがちです。その結果、意図しない型のデータを渡してしまい、思わぬバグにつながることがあります。
そこで登場するのがRBS(Ruby Signature)です。RBSはRubyコードとは別に用意する「型の設計図ファイル」で、「このクラスのこのメソッドは、こういう型のデータを受け取って、こういう型のデータを返すよ」と人間とツールの両方に教えてくれます。RBSファイル(拡張子は.rbs)はあくまで設計図なので、そのまま実行されることはありませんが、後で行う静的型チェックの土台になります。
まずはイメージしやすいように、Rubyのコード例を見てみましょう。名前を受け取ってあいさつ文を返すだけの、とてもシンプルなクラスです。
class User
def initialize(name)
@name = name
end
def greet
"こんにちは、#{@name}さん!"
end
end
このクラスに対してRBSファイルを作ると、次のように書けます。
class User
def initialize: (String) -> void
def greet: () -> String
end
ここで「(String) -> void」というのは、「initializeメソッドは、String型(文字列)の値を1つ受け取って、呼び出し元には何も返さない(void)」という意味です。そして「() -> String」は「greetメソッドは引数を受け取らず、その代わりにString型(文字列)を必ず返す」ということを表しています。
実際のRubyコードだけを眺めていると、「nameには文字列が入る想定なのか?それとも別のオブジェクトも来るのか?」と読み手によって解釈が分かれることがあります。しかしRBSで型をはっきり書いておくと、「このクラスは文字列の名前を前提に設計されているんだな」とひと目で分かります。プログラミング未経験の人でも、「この箱にはこういう種類の値だけを入れてください」というルールを書いたメモだと思えば、RBSが担っている役割をイメージしやすいはずです。
2. typeprofを使ってRBSを自動生成してみよう
「RBSファイルって自分で全部書くの?」と感じる人は多いと思います。実際、最初から細かい型情報を手作業で書くのは大変です。そこで心強い味方になるのが、Rubyに標準搭載されているtypeprofというツールです。typeprofはRubyのコードを読み取り、「このメソッドはこんな値を受け取って、こんな値を返しているんじゃないかな?」と推測し、RBSファイルの元となる情報を自動で作成してくれます。
イメージとしては、コードの“性格診断”をしてくれるようなもので、自分が書いたコードの型をざっくり把握したいときにとても便利です。初心者でも難しい設定なしで使えるので、まずは気軽に試してみるのがおすすめです。
例として、前の章で作ったuser.rbを typeprof に渡してみましょう。ターミナル(黒い画面)に次のように入力します。
typeprof user.rb
すると、typeprof がコードを分析して、次のような推論結果を表示します。
# Classes
class User
def initialize: (String) -> void
def greet: () -> String
end
この結果は、「initializeは文字列を受け取って何も返さない」「greetは引数なしで文字列を返す」という推測を示しています。つまり、typeprofは Ruby のコードを読み解き、型のヒントとなる情報を自動で作ってくれる便利なツールなのです。
プログラミングに慣れていない人でも、「自分のコードがどんな型として扱われているのか」を目で見て確認できるのは大きな安心材料になります。手作業でRBSを書く前に、まずはtypeprofで雛形を作ってみると理解がぐっと深まります。
3. steepで静的型チェックを行う
steep(スティープ)は、RBSファイルを使って実際のRubyコードと照らし合わせながら「型の不整合がないか」をチェックしてくれるツールです。いわば、“Rubyコードのスペルチェック”のようなものです。
まず、steepをインストールしましょう。ターミナルで次を実行します。
gem install steep
次にプロジェクトのルートで初期設定を行います。
steep init
これでSteepfileという設定ファイルが作られます。steepはこのファイルを読み込み、どのRubyファイルとRBSファイルをチェックするかを判断します。
設定ができたら、次のコマンドで型チェックを実行します。
steep check
たとえば、Ruby側で次のようなミスをしていた場合を見てみましょう。
class User
def initialize(name)
@name = name
end
def greet
123 # 本当は文字列を返すべきなのに数字を返している!
end
end
この状態でsteep checkを実行すると、次のようなエラーが出ます。
# Type error: Expected String but got Integer
このように、Rubyの柔軟さを保ちながらも、型のミスを事前に発見することができます。特に大規模なアプリ開発では非常に役立ちます。
4. typeprofとsteepの違いを整理しよう
ここで、二つのツールの役割を整理してみましょう。
- typeprof:既存のRubyコードから型を推測して、RBSファイルを自動生成するツール。
- steep:RBSファイルを使って、Rubyコードの型を静的にチェックするツール。
つまり、typeprofで型情報を作り、steepで確認するという流れになります。
この2つを組み合わせることで、「Rubyは動的型付けだから型がわからない」という不安を大きく減らすことができます。
5. RBSを使うと何がうれしいの?
RBSを使うことで、以下のようなメリットがあります。
- コードを読むときに「このメソッドは何を返すか」がすぐに分かる。
- チーム開発で他の人が作ったコードでも安心して使える。
- 型エラーを事前に発見できるため、バグの減少につながる。
特に、Ruby on Railsのような大きなフレームワークを使う場合や、他人と共同でプログラムを作る場合に、RBSとsteepは非常に心強い味方になります。
6. Rubyにも型の世界を取り入れよう
Rubyは柔軟で使いやすい言語ですが、RBSやsteepを使うことで、静的型付け言語のような「安心感」も手に入ります。特にこれからプログラムを長く続けていく人にとって、RBSの考え方は大切な基礎になります。
次のステップとして、自分のRubyプロジェクトで実際にtypeprofやsteepを動かしてみましょう。小さなスクリプトでも、型のチェックができるとぐっと理解が深まります。
まとめ
今回の学習では、Rubyの動的型付けという特徴をふまえながら、RBSやtypeprof、そしてsteepを使った静的型チェックの基本的な流れを丁寧に振り返りました。Rubyはコードを書くときに型を宣言しなくても動作する柔軟さが魅力ですが、プロジェクトが大規模化すると「このメソッドはどんな引数を受け取るのか」「返り値は何なのか」といった情報が不透明になり、理解や保守が難しくなります。そこで登場するのがRBSという“型の設計図”であり、Rubyコードと併せて読み解くことで、プログラム全体の構造がより明確になります。とくにクラスやメソッドの役割を整理したいとき、RBSで型情報を外部化することは大きな助けになります。さらにtypeprofを活用すれば、既存のRubyコードから自動的に型を推論し、RBSファイルを生成することができ、手作業で型を書く手間を大幅に軽減できます。 型情報がそろったら、steepを使った静的型チェックによって実際のRubyコードとRBSを突き合わせ、型の不一致や設計上の矛盾を早期に発見できます。開発の初期段階でこのチェックを取り入れることで、潜在的なエラーを未然に防ぎ、長期的な品質向上につながります。とくにチーム開発では、RBSが共通の認識となり、メンバー同士が安心してコードを読み書きできるという大きな利点があります。静的型チェックを導入するということは、単に型を厳格にするだけではなく、コード全体の見通しや再利用性を高めるための重要な手段でもあります。 ここであらためて、RBS・typeprof・steepそれぞれの役割をふり返ってみましょう。RBSは明示的に型を設計し、クラスやメソッドの構造を整理する“型の辞書”のような存在です。typeprofはRubyコードから推論してその辞書を自動生成するアシスタントのようなツール。そしてsteepは辞書と実際のコードに矛盾がないかをチェックする検査官のような役割です。この三つをセットで使うことで、Rubyでも静的型言語に近い安心感を得ながら、動的型付けの自由さはそのまま保つという、バランスの取れた開発環境が整います。 以下は、まとめとして示す簡単なサンプルプログラムです。記事内で扱ったclassやタグと同じような構造を用いています。
サンプル(RubyコードとRBS)
class Order
def initialize(id, total)
@id = id
@total = total
end
def info
"注文ID: #{@id} 合計金額: #{@total}"
end
end
class Order
def initialize: (Integer, Integer) -> void
def info: () -> String
end
このようにRubyのコードとRBSを並べると、それぞれのメソッドが受け取る型や返す型が視覚的に理解しやすくなり、型の整合性に自信を持ちながらプログラムを書くことができます。特に業務アプリケーションや長期運用されるサービスでは、型情報が豊富であるほど安心してコードを引き継ぐことができます。動的型付け言語であるRubyでも、RBSを活用することで静的な側面を取り込み、コード品質の向上に大きく寄与できます。
生徒
「先生、今日の内容でRubyでも型を扱える理由がよくわかりました!動的型付けの自由さは残しつつ、RBSで型を明確にすることで安心感が増すんですね。」
先生
「その通りです。Rubyは柔軟で書きやすい言語ですが、大きな開発では型情報があるだけで理解がぐっと楽になります。RBSはあくまで“外側から型を付ける”仕組みなので、Rubyの書きやすさを損ないません。」
生徒
「そしてtypeprofで自動生成できるのが助かりますね。わざわざ全部を書かなくても推論してくれるのが便利です。」
先生
「そのとおり。さらにsteepを組み合わせれば、実際のコードとRBSが矛盾していないかを簡単にチェックできます。バグの早期発見にもつながります。」
生徒
「なるほど、三つを合わせて使うことでRubyでも型安全な開発ができるわけですね!」
先生
「そうです。型を味方につけることで、Rubyの開発はもっと強力で読みやすく、保守しやすくなりますよ。」