`changeset()` 规范需要逻辑上不可为空的可空类型

`changeset()` spec requires nullable type which is not logically nullable

我有以下代码:

defmodule Foo do
  @moduledoc false

  use Ecto.Schema

  import Ecto.Changeset


  @type t :: %__MODULE__{
          id: integer(),
          foo: String.t(),
          baz_id: String.t(),
          bar: String.t() | nil
        }

  embedded_schema do
    field :foo, :string
    field :bar, :string
  end

  @spec changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
  def changeset(bae \ %__MODULE__{}, attrs) do
    bae
    |> cast(attrs, @fields)
    |> unique_constraint(:baz_id)
  end
end
根据 @type 的定义,

foobaz_id 不应该是 nil。 但是,dialyzer 正在抱怨(给定的 @spec),因为默认值 %__MODULE__{} 会将它们设置为 nil.

如果我将 @type 定义替换为:

...
  @type t :: %__MODULE__{
          id: integer() | nil,
          foo: String.t() | nil,
          baz_id: String.t() | nil,
          bar: String.t() | nil
        }
...

然后 dialyzer 不会抱怨,但我不再理解某些字段不可为空的想法。

有什么优雅的方法可以让 changeset() 按目前的方式工作,并避免 dialyzer 抱怨这个特定用途?

好吧,您明确指定了违反透析器合同的默认参数(架构是一个没有默认值的裸),这就是透析器抱怨的原因。

目前还不清楚,如果空 %__MODULE__{} 不允许,您应该如何处理它,但回答上述问题时,解决方法是默认接受 nil 参数。

@spec changeset(
    nil | t() | Ecto.Changeset.t(), map()
  ) :: Ecto.Changeset.t()
def changeset(bae \ nil, attrs) do
  bae
  |> Kernel.||(%__MODULE__{})
  |> cast(attrs, @fields)
  |> unique_constraint(:baz_id)
end