jc00ke
Typespec for map w/both required and optional keys
Expanding on this topic: Map typespec question
Let’s say I have a map with required and optional keys. I’d like to document both, but based on the docs it seems like you can’t denote a specific key is optional. Where else do people document these optional keys?
@type params :: %{foo: String.t, optional(:atom) => integer()}
@doc """
Does a thing
## Examples
iex> do_thing(params)
{:ok, "thing_done"}
## Parameters
%{
foo: required(string),
bar: optional(integer)
}
"""
@spec do_thing(params) :: {:ok, String.t}
def do_thing(...), do: {:ok, "thing_done"}
With that example in mind, how do I specify in the @type params declaration that :bar is optional? Just leave it out and document its optionality in the @doc?
Thanks for the help!
Marked As Solved
NobbZ
Literals are their own type. So optional(:bar) is exactly that.
Also Liked
OvermindDL1
Yep, use the union operator |. ![]()
I.E. you specify the whole structure twice with each variation, like:
%{
account_number: String.t(),
:counterparty_id => integer(),
} | %{
account_number: String.t(),
receiver_account_number: String.t()
}
It’s a bit of a pain and combinatorially explosive, but it works. ![]()
soup
A potential stumbling block: even though the keys look like atoms, you can’t mix the x: and => when using optional. I assume this is because optional(:bar) resolves to a non-atom type.
You might get the somewhat cryptic error:
@type params :: %{
foo: String.t(),
optional(:bar) => integer() # "syntax error before: optional"
}
You must convert to:
@type params :: %{
foo => String.t(),
optional(:bar) => integer()
}
E: see below, or
@type params :: %{
optional(:bar) => integer(),
foo: String.t()
}
LostKobrakai
The syntax sugar for keyword lists as well as for atom keys in maps is only allowed trailing to elements not using the syntax sugar within the same map/list. Though I‘m not sure if the same does work in typespecs.
blatyo
Perhaps do it with the value.
@type parmas :: %{foo: String.t, bar: integer() | nil}
Or maybe:
@type optional_integer :: integer() | nil
@type parmas :: %{foo: String.t, bar: optional_integer()}
jc00ke
%{
account_number: String.t(),
optional(:counterparty_id) => integer(),
receiver_account_number: String.t() unless :counterparty_id set
}
Since I have your attention (
) is there a way to specify something is required if another key is not present? In this case, you can either have a :counterparty_id or several :receiver_* pairs.







