2

(请注意,这在 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})

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

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

谢谢!

4

1 回答 1

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

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

于 2019-06-30T21:11:23.250 回答