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

时间:2019-06-30 20:58:53

标签: ruby sorbet

(请注意,这在sorbet.run上是不可复制的,据我所知,它只能在本地的Sorbet上复制)

我希望我可以使用Typed Structs feature创建一个方法签名,其中一个参数是一个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})

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

documentation on generics提到了哈希,但是似乎建议仅在哈希的键和值都是相同类型的情况下才可以使用它们(例如,Hash<Symbol, String>要求所有键都是Symbols,所有值都是Strings) ),并且(据我所知)没有提供任何方法来定义具有特定键的哈希。

谢谢!

1 个答案:

答案 0 :(得分:2)

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

  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
  1. 像您一样使用T::Struct。这迫使您使用MyStruct.new(prop1: x, prop2: y, ...)
  2. 调用该方法

所有这些参数都是有效的,其中4和5是为您提供最大类型安全性的参数。在这两个参数中,呼叫者最灵活的是4个,但是您知道Sorbet在短期/中期不会改变支持的那个是5个。