[Ruby3.0]型推論、型検査（Typeprof、Steep）を試してみる

# はじめに
今まで2系で使っていたプロジェクトを3系にバージョンアップするにあたって、型検査導入しようと思って調べました。

# 使用ライブラリ
おそらく型検査とか型推論とか調べると、**Typeprof**と**Steep**が出てくるんですが、最初使い分けが分かりにくいのですが、以下資料のこの画像が全てかなと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/096f32eb-472a-40a3-8e03-a99244314503.png)
※「[Ruby 3の型推論やってます](https://www.slideshare.net/mametter/ruby-3-232144041)」より引用

この、ruby-type-profiler = TypeProfですね。

つまり、TypeProfは実コードからrbsファイル（型が記述されたファイル）を生成し、Steepはその型定義から実コードを型検査するという役割です。

もう少し細かく解説するなら、

- TypeProfのメイン機能は型推論なので、例えば[VSCodeのプラグイン](https://marketplace.visualstudio.com/items?itemName=mame.ruby-typeprof)入れればコード補完や型の間違いを指摘してくれる（ただ、型推論の精度は完璧ではないので適宜自分で型ファイルを修正する）
- rbsファイルを1から自分で作ればTypeProf使わなくてもSteepで型検査できる。こちらも[プラグイン](https://marketplace.visualstudio.com/items?itemName=soutaro.steep-vscode)を入れれば型検査の結果をIDE上で表示してくれます。

# TypeProf
以下の記事を参考にTypeProf導入します。

[Ruby3.1 静的解析の導入で開発体験を向上させる (RBS, TypeProf)｜Offers Tech Blog](https://zenn.dev/offers/articles/20220509-ruby3-type-interpretation)

色々頑張ったんですが、私の環境だとVSCodeのプラグインは動いてくれませんでしたが、typeprofコマンドで型ファイルを生成するところまではできました。

# Steep
以下の記事を参考にTypeProf導入します。

[Steepを使ってRubyの型検査に入門する](https://qiita.com/kettomorrow/items/8ccada8a4c9eac85b7ad)

こちらはVSCodeのプラグインが無事動作してくれたので、ちょっと確認してみます。
上記の記事のサンプルコードを使用させて頂きます。上記記事では自分で書いたrbsファイルを使用していました、今回はtypeprof導入済みのため、こちらで生成したrbsファイルを利用してみます。

```ruby
class Person
  attr_reader :name
  attr_reader :gender

  def initialize(name:, gender:)
    @name = name
    @gender = gender
  end
end
```
こちらのクラスが記述されているファイルをtypeprofで読み込むと以下のようなrbsファイルが得られます。

```rb
# TypeProf 0.21.7

# Classes
class Person
  attr_reader name: untyped
  attr_reader gender: untyped
  def initialize: (name: untyped, gender: untyped) -> void
end
```

まあ、nameもgenderも何が入るかは全くわからないので、当たり前と言えば当たり前のようにuntypedが入りますね。
一つメソッドを追加して、改めてrbsファイルを生成します。

```ruby
class Person
  attr_reader :name
  attr_reader :gender

  def initialize(name:, gender:)
    @name = name
    @gender = gender
  end

  def hello
    "hello, #{name}. You are #{gender}."
  end
end
```

```rb
# TypeProf 0.21.7

# Classes
class Person
  attr_reader name: untyped
  attr_reader gender: untyped
  def initialize: (name: untyped, gender: untyped) -> void
  def hello: -> String
end
```

helloの返り値がStringということは推定されていますが、nameもgenderもIntegerが入ってきても特に問題なく処理されるのでuntypedになっていますね。

この状態で、Personをただnewすると、引数が足らないというエラーがちゃんと出てくれますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/5cfe393c-0246-286f-ca8c-6bcdc6dea972.png)

`steep check`コマンドをコンソールで叩いても同様のエラーが出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/d19b58e9-ca73-97e2-10eb-429acc8f65cb.png)


あとは、気になることとしてrbsファイルに書いてあるメソッドが実装されていない場合どうなるか、つまりinterface的に使えるのかちょっと確認してみます。

```rb
# TypeProf 0.21.7

# Classes
class Person
  attr_reader name: untyped
  attr_reader gender: untyped
  def initialize: (name: untyped, gender: untyped) -> void
  def hello: -> String
  def male?: -> bool
end
```

`steep check`の結果は、以下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312001/0f25c126-dc8b-4c61-54c6-965e86644883.png)

まあ当たり前といえば当たり前ですね。依存関係をチェックしているわけではなく、間違った型の使われていないかチェックしているのでinterface的な使い方は難しいっぽいです。

これ入れてくれると結構良さげかなと思いました。動的言語にわざわざ型チェック入れてるんだからついでにinterface入れてくれても的な。

# まとめ
Rubyの型検査に関してはまだ発展途上な感じがしていて、もうちょっと使いやすくなれば導入可能かなと思いました。
現状、1から導入するのであれば、そこまでハードル高くないですが、既存プロジェクトに適用するとなるとかなり大変だなぁと。