如何在 Sorbet 中为具有属性的散列定义签名?
How to define a signature for a hash with attributes in 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})
本质上,当您对该文件进行类型检查时,它会抱怨传递了一个散列,而这正是预期的结构。我的问题是:如何为具有特定参数的散列定义有效签名?结构显然在这里不起作用。虽然我没有尝试过 Shapes,但根据文档,它们非常有限,所以如果可能的话我宁愿不要使用它们。
documentation on generics 提到了散列,但似乎暗示它们只能在散列的键和值都是相同类型的情况下使用(例如 Hash<Symbol, String>
要求所有键都是符号,所有值是字符串),并且没有提供任何方式(据我所知)来定义具有特定键的散列。
谢谢!
基本上,您必须选择多种方式之一(您已经提到的三种方式):
- 使用
T::Hash[KeyType, ValueType]
。这允许您在调用将其作为参数的方法时使用 {}
语法,但强制您为每个条目使用相同类型的键和值。
- 使用
T::Hash[KeyType, Object]
。这在值的类型上更灵活一些......但是你丢失了类型信息。
- 使用
T::Hash[KeyType, T.any(Type1, Type2, ...)
。这是 1 和 2 之间的中间地带。
- 使用形状。正如文档所说,功能可能会改变并且是实验性的。这是在不向调用者强加使用
T::Struct
的情况下模拟这样的东西的最好方法:
sig { params(options: {x: Integer}).void }
def method(options)
puts options[:x]
end
- 像您一样使用
T::Struct
。这会强制您使用 MyStruct.new(prop1: x, prop2: y, ...)
调用该方法
它们都是有效的,其中 4 和 5 是类型安全性最高的。在这两者中,4 对调用者来说是最灵活的,但是 5 是你知道的 Sorbet
不会改变 short/medium 术语中的支持。
(请注意,这在 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})
本质上,当您对该文件进行类型检查时,它会抱怨传递了一个散列,而这正是预期的结构。我的问题是:如何为具有特定参数的散列定义有效签名?结构显然在这里不起作用。虽然我没有尝试过 Shapes,但根据文档,它们非常有限,所以如果可能的话我宁愿不要使用它们。
documentation on generics 提到了散列,但似乎暗示它们只能在散列的键和值都是相同类型的情况下使用(例如 Hash<Symbol, String>
要求所有键都是符号,所有值是字符串),并且没有提供任何方式(据我所知)来定义具有特定键的散列。
谢谢!
基本上,您必须选择多种方式之一(您已经提到的三种方式):
- 使用
T::Hash[KeyType, ValueType]
。这允许您在调用将其作为参数的方法时使用{}
语法,但强制您为每个条目使用相同类型的键和值。 - 使用
T::Hash[KeyType, Object]
。这在值的类型上更灵活一些......但是你丢失了类型信息。 - 使用
T::Hash[KeyType, T.any(Type1, Type2, ...)
。这是 1 和 2 之间的中间地带。 - 使用形状。正如文档所说,功能可能会改变并且是实验性的。这是在不向调用者强加使用
T::Struct
的情况下模拟这样的东西的最好方法:
sig { params(options: {x: Integer}).void }
def method(options)
puts options[:x]
end
- 像您一样使用
T::Struct
。这会强制您使用MyStruct.new(prop1: x, prop2: y, ...)
调用该方法
它们都是有效的,其中 4 和 5 是类型安全性最高的。在这两者中,4 对调用者来说是最灵活的,但是 5 是你知道的 Sorbet
不会改变 short/medium 术语中的支持。