This is mostly possible already, like so:
@type params(k, v) :: %{k => v}
@spec handle_params(params(s, s)) :: ok when s :: String.t
While Erlang doesn't support expressing guards on the generic parameters in a type definition, Elixir could just convert such definitions to the Erlang compatible form by replacing the type variables with the concrete type. In general though I tend to deal with the verbosity problem by extracting the noisy type name into its own definition that is more easily used, e.g.:
@type param_key :: String.t
@type param_val :: String.t
@type params :: %{param_key => param_val}
This has the advantage of being easier to read and easier to change in the future (the `params` type doesn't need to change if the range of types for keys/values changes).
Paul