如何在另一个 T::Struct 中重用 T::Struct 结构
How do I reuse a T::Struct structure in another T::Struct
我有以下内容:
class Coordinate < T::Struct
const :x, Integer
const :y, Integer
end
class ThreeDCoordinate < T::Struct
const :x, Integer
const :y, Integer
const :z, Integer
end
我想要的是让我的 ThreeDCoordinate
继承 Coordinate
的 x
和 y
,这样我就不必在 ThreeDCoordinate
中重写它们.我怎样才能做到这一点?
据我所知,Sorbet 并不完全支持您所要求的功能。
但是,我正在使用接口来实现类似的打字目标:
module Coordinate
extend T::Helpers
extend T::Sig
interface!
sig { abstract.returns(Integer) }
def x; end
sig { abstract.returns(Integer) }
def y; end
end
module ThreeCoordinate
extend T::Helpers
extend T::Sig
include Coordinate
interface!
sig { abstract.returns(Integer) }
def z; end
end
class ThreeDLocation < T::Struct
extend T::Sig
include ThreeCoordinate
# Will be complained by Sorbet because of missing interface
const :x, Integer
end
class ThreeDCenter < T::Struct
extend T::Sig
include ThreeCoordinate
sig { override.returns(Integer) }
def x
0
end
sig { override.returns(Integer) }
def y
0
end
sig { override.returns(Integer) }
def z
0
end
end
在 Sorbet Playground 上试试看 Sorbet 静态检查如何帮助确保输入。
虽然代码中可能会有一些重复,但我们已经达到了类实现ThreeCoordinate
的目的,保证也有Coordinate
的接口。
另外,采用这种方式,实现起来更加灵活。我们还可以实现类似上面 ThreeDCenter
的东西,而不必局限于仅使用 Struct 属性。
有一种方法可以做到这一点,使用 T::InexactStruct
,但您将不得不放弃能够强类型化结构的初始化程序:
# typed: true
class Coordinate < T::InexactStruct
const :x, Integer
const :y, Integer
end
class ThreeDCoordinate < Coordinate
const :z, Integer
end
coord = Coordinate.new(x: 2, y: 3)
T.reveal_type(coord.x)
T.reveal_type(coord.y)
threeD = ThreeDCoordinate.new(x: 2, y: 3, z: 4)
T.reveal_type(threeD.x)
T.reveal_type(threeD.y)
T.reveal_type(threeD.z)
# Note that the constructors are not typed anymore:
Coordinate.new(x: "foo", y: :bar) # This should fail but doesn't
T::Struct
和子类化的问题在于,Sorbet 会为您的结构创建一个初始化程序,该初始化程序会考虑其所有已声明的字段。因此,对于 Coordinate
,初始化程序具有签名 params(x: Integer, y: Integer).void
,但对于 ThreeDCoordinate
,它具有签名 params(x: Integer, y: Integer, z: Integer).void
。现在这些签名彼此不兼容,所以 Sorbet 不允许您从另一个继承一个。
T::InexactStruct
允许您放弃构造函数中的强类型,并换取能够对类型结构进行继承。
我有以下内容:
class Coordinate < T::Struct
const :x, Integer
const :y, Integer
end
class ThreeDCoordinate < T::Struct
const :x, Integer
const :y, Integer
const :z, Integer
end
我想要的是让我的 ThreeDCoordinate
继承 Coordinate
的 x
和 y
,这样我就不必在 ThreeDCoordinate
中重写它们.我怎样才能做到这一点?
据我所知,Sorbet 并不完全支持您所要求的功能。
但是,我正在使用接口来实现类似的打字目标:
module Coordinate
extend T::Helpers
extend T::Sig
interface!
sig { abstract.returns(Integer) }
def x; end
sig { abstract.returns(Integer) }
def y; end
end
module ThreeCoordinate
extend T::Helpers
extend T::Sig
include Coordinate
interface!
sig { abstract.returns(Integer) }
def z; end
end
class ThreeDLocation < T::Struct
extend T::Sig
include ThreeCoordinate
# Will be complained by Sorbet because of missing interface
const :x, Integer
end
class ThreeDCenter < T::Struct
extend T::Sig
include ThreeCoordinate
sig { override.returns(Integer) }
def x
0
end
sig { override.returns(Integer) }
def y
0
end
sig { override.returns(Integer) }
def z
0
end
end
在 Sorbet Playground 上试试看 Sorbet 静态检查如何帮助确保输入。
虽然代码中可能会有一些重复,但我们已经达到了类实现ThreeCoordinate
的目的,保证也有Coordinate
的接口。
另外,采用这种方式,实现起来更加灵活。我们还可以实现类似上面 ThreeDCenter
的东西,而不必局限于仅使用 Struct 属性。
有一种方法可以做到这一点,使用 T::InexactStruct
,但您将不得不放弃能够强类型化结构的初始化程序:
# typed: true
class Coordinate < T::InexactStruct
const :x, Integer
const :y, Integer
end
class ThreeDCoordinate < Coordinate
const :z, Integer
end
coord = Coordinate.new(x: 2, y: 3)
T.reveal_type(coord.x)
T.reveal_type(coord.y)
threeD = ThreeDCoordinate.new(x: 2, y: 3, z: 4)
T.reveal_type(threeD.x)
T.reveal_type(threeD.y)
T.reveal_type(threeD.z)
# Note that the constructors are not typed anymore:
Coordinate.new(x: "foo", y: :bar) # This should fail but doesn't
T::Struct
和子类化的问题在于,Sorbet 会为您的结构创建一个初始化程序,该初始化程序会考虑其所有已声明的字段。因此,对于 Coordinate
,初始化程序具有签名 params(x: Integer, y: Integer).void
,但对于 ThreeDCoordinate
,它具有签名 params(x: Integer, y: Integer, z: Integer).void
。现在这些签名彼此不兼容,所以 Sorbet 不允许您从另一个继承一个。
T::InexactStruct
允许您放弃构造函数中的强类型,并换取能够对类型结构进行继承。