TypeCounselor

An experimental library for suggesting type specifications based on Elixir maps and structures.

iex> map_1 = %{identifier: "IBM5100"}
iex> map_2 = %{identifier: 5100}
iex> TypeCounselor.suggest([map_1, map_2])
"%{identifier => String.t() | :non_neg_integer}"

Usage

1. Installation

Add the package to your mix.exs:

def deps do
  [
    {:type_counselor, "~> 0.1.0", only: :test}
  ]
end

2. Instrument the codebase

Manually instrument places where structs are created to call TypeCounselor.add/2.

Example:

def populate_users(user_ids) do
  Enum.map(user_ids, fn user_id -> 
    case SpotifyClient.fetch_user(user_id) do
      {:ok, user_info} ->
        # Note the `TypeCounselor.add/2` call below
        TypeCounselor.add(:user, %User{
          name: user_info[:username],
          profile: user_info[:profile_url],
          flags: user_info[:flags]
        })

      {:error, _} = result -> 
        result
    end
  end)
end

3. Run tests and collect type suggestions

# Run tests and spawns iex for collecting type suggestions
$ iex -S mix test

Erlang/OTP 25 [erts-13.0.3] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit:ns]

Compiling 5 files (.ex)
Generated type_counselor app
.......
Finished in 0.01 seconds (0.01s async, 0.00s sync)
7 tests, 0 failures

After tests are run, iex should be spawned:

iex> suggestions = TypeCounselor.fetch(:user)
iex> File.write!("user.exs", suggestions)

The content of the created user.exs will contain type suggestions for user according to user_info content values during test runtime:

%{
  name => String.t() | nil,
  profile => String.t(),
  flags => list(:enabled | :disabled)
}