如何在 Sorbet 中为具有属性的散列定义签名?

Con*_*hea 2 ruby sorbet

(请注意,这不能在 sorbet.run 上重现,据我所知,它只能用 Sorbet 的本地副本重现)

我希望我可以使用Typed Structs 功能来创建一个方法签名,其中一个参数是options散列,但这不起作用:

# typed: true
require 'sorbet-runtime'
extend T::Sig

class OptionsStruct < T::Struct
  prop :x, Integer, default: 1
end

sig { params(options: OptionsStruct).void }
def method(options)
  puts options.x
end

# This works
method(OptionsStruct.new({x: 2}))

# This causes the typechecker to throw.
method({x: 2})
Run Code Online (Sandbox Code Playgroud)

本质上,当您键入检查此文件时,它会抱怨在需要 Struct 时传入哈希值。我的问题是:如何为具有特定参数的哈希定义有效签名?结构在这里显然不起作用。虽然我没有尝试过 Shapes,但根据文档,它们非常有限,所以如果可能的话,我宁愿不使用它们。

关于泛型文档提到了哈希,但似乎表明它们只能在哈希的键和值都是相同类型的情况下Hash<Symbol, String>才能使用(例如,要求所有键都是符号,所有值都是字符串),并且不提供任何方式(据我所知)用特定的键定义一个散列。

谢谢!

mar*_*one 5

从本质上讲,您必须选择采用多种方式之一(您已经提到了三种方式):

  1. 使用一个T::Hash[KeyType, ValueType]. 这允许您{}在调用将其作为参数的方法时使用该语法,但强制您对每个条目使用相同类型的键和值。
  2. 使用一个T::Hash[KeyType, Object]. 这在值的类型上更加灵活……但是您会丢失类型信息。
  3. 使用一个T::Hash[KeyType, T.any(Type1, Type2, ...). 这是介于 1 和 2 之间的中间地带。
  4. 使用形状。正如文档所说,功能可能会改变并且是实验性的。这是在不强加T::Struct给调用者使用的情况下对这样的东西进行建模的最好方法:
sig { params(options: {x: Integer}).void }
def method(options)
  puts options[:x]
end
Run Code Online (Sandbox Code Playgroud)
  1. T::Struct像你一样使用 a 。这迫使您调用该方法MyStruct.new(prop1: x, prop2: y, ...)

它们都是有效的,其中 4 和 5 是为您提供最多类型安全性的。在这两者中,4 对调用者来说是最灵活的,但 5 是你知道Sorbet在短期/中期不会改变支持的那个。