Type Checking Ruby with Solargraph
Tags: ruby, vscode
Published: 2020-05-21 19:46:52

Proper type checking can be challenging in dynamically typed languages like Ruby. Nonetheless, I wound up going down this road as a side effect of my work on [Solargraph](https://solargraph.org). The current version (0.39.7 at time of writing) now ships with a type checking tool. While it's still a work in progress, it's mature enough that I use it regularly in my own projects. In this post, I'll discuss the history behind it and provide a brief introduction to how it works.

## Origins

I started Solargaph several years ago out of an interest in improving the tooling ecosystem around Ruby. At the time, I was developing an Eclipse plugin for [Gamefic](https://gamefic.com), another Ruby project of mine. The plugin quickly grew from a domain-specific application to a general-purpose Ruby tool. After the release of Visual Studio Code and the introduction of the Language Server Protocol, I decided to turn Solargraph into an editor-agnostic language server.

Most of my early work focused on capabilities like autocompletion and inline documentation. My early approach was to provide basic mapping of class and method definitions augmented by YARD annotation. Over time, the mapping evolved to provide type inference through static analysis.

As the static analysis improved, it seemed like a natural progression to add type checking as a diagnostics feature.

## How It Works

The type checker is somewhat similar to [Sorbet](https://sorbet.org/), with the key difference being that Solargraph uses [YARD](https://yardoc.org) comments for annotation. Type safety isn't enforced at run-time. My approach has been to treat type discrepancies as code smells. You can use the checker to report problems in your editor or add it to your CI/CD workflow with varying degrees of strictness.

Code smells that the type checker can detect include the following:

- Invalid method call arity (e.g., not enough or too many arguments)
- Undefined or unknown types
- Unresolved method or variable names
- Incomplete documentation (e.g., methods without `@return` tags)
- `@return` tags that disagree with types inferred from code
- `@param` tags that disagree with types inferred from method arguments

The type checker can run at four levels: `normal`, `typed`, `strict`, and `strong`. See the [type checker documentation](https://solargraph.org/guides/type-checking) for more information about the rules enforced at each level.

## Using the Type Checker

There are two primary ways to use Solargraph's type checker: the command-line tool and the language server's diagnostics capability.

### The Command Line

You can run the type checker from the command line in the root directory of your project. We'll use a directory containing the following `example.rb`:

```ruby
class Example
  # @return [String]
  def foo
    "foo"
  end

  # @return [InvalidClassName]
  def bar
    "bar"
  end
  
  # @return [Integer]
  def baz
    "baz"
  end
end

Example.new.foo.downcase
Example.new.foo.uppercase
```

Run the type checker:

```
$ solargraph typecheck
./example/example.rb:8 - Unresolved return type InvalidClassName for Example#bar
```

The default `normal` level enforces the bare minimum of type safety, so the only error it finds is the unrecognized `InvalidClassName` class. At the `typed` level or higher, it'll catch the type discrepancy in `Example#baz`:

```
$ solargraph typecheck --level typed
./example/example.rb:8 - Unresolved return type InvalidClassName for Example#bar
./example/example.rb:13 - Declared return type Integer does not match inferred type String for Example#baz
2 problems found.
```

At the `strict` level, it will also catch the unknown `uppercase` method:

```
$ solargraph typecheck --level strict
./example/example.rb:8 - Unresolved return type InvalidClassName for Example#bar
./example/example.rb:13 - Declared return type Integer does not match inferred type String for Example#baz
./example/example.rb:19 - Unresolved call to uppercase
3 problems found.
```

### Diagnostics in VS Code

If you use the [Solargraph extension for VS Code](https://marketplace.visualstudio.com/items?itemName=castwide.solargraph), you can configure your editor to use the type checker for diagnostics by adding it to your project's `.solargraph.yml` file:

```
reporters:
- typecheck
```

You can also specify a level:

```
reporters:
- typecheck:strict
```

Type errors will appear in the Problems console.

![The Problems console](https://dev-to-uploads.s3.amazonaws.com/i/fl9yixy4ra96twdboj6r.png)

This feature should also work in any other language client that supports diagnostics.

## Further Reading

Check the following links to learn more about Solargraph and try it for yourself.

- [The Solargraph website](https://solargraph.org)
- [The GitHub repo](https://github.com/castwide/solargraph)
- [Solargraph for VS Code](https://marketplace.visualstudio.com/items?itemName=castwide.solargraph)