Hi folks, I'm not even sure if this is possible nor if it was raised before nor if there is already another way of doing something similar. But this is something that I've been thinking a lot recently and I'd like to know if it is possible and desirable. If it is, I can help with a PR later on.
The "problem":It is usual to when we have a Struct (specially Ecto.Schemas) to define a `@type t()` with the fields. Let's say we have a Struct with a few fields like this:
```
defmodule MyStruct do
defstruct [:a, :b, :c]
@type t() :: %__MODULE__{
a: integer() | nil,
b: integer() | nil,
c: integer() | nil
}
```
Now imagine I want to define a function that receives a struct but requires one of these fields to be non null. What we have to do right now is to:
```
@type my_struct_with_a() :: %MyStruct{
a: integer(),
b: integer() | nil,
c: integer() | nil
}
```
(Ok, maybe in some cases we do not need to redefine all the other fields since they might be irrelevant, but let's assume we actually want to type everything)
The proposal:For map values when we just want to change one field we can do something like: ```%MyStruct{my_struct | a: 1}```, but for types this is not possible.
My proposal would be to have something like this:
```
@type my_struct_with_a() :: %MyStruct{MyStruct.t() | a: integer()}
```
That would generate a type with all fields copied from `MyStruct.t()` but with `:a` changed to `integer()` instead of `integer() | nil`.
The caveats I see is what to do when the type is not just a map type (maybe it is a sum type, maybe it is not even a map, etc).
What do you folks think about this?
Thanks,
Bernardo Amorim