【问题标题】:`changeset()` spec requires nullable type which is not logically nullable`changeset()` 规范要求逻辑上不可为空的可空类型
【发布时间】:2021-02-16 21:09:38
【问题描述】:

我有以下代码:

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

foobaz_id 不应为 nil,根据 @type 定义。 但是,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 抱怨这种特定用途的优雅方式?

【问题讨论】:

    标签: types elixir dialyzer typespec


    【解决方案1】:

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

    目前尚不清楚,一旦不允许,您应该如何处理空的 %__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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-05
      • 2010-10-10
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多