如何在另一个 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 继承 Coordinatexy,这样我就不必在 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

Sorbet Playground Link

T::Struct 和子类化的问题在于,Sorbet 会为您的结构创建一个初始化程序,该初始化程序会考虑其所有已声明的字段。因此,对于 Coordinate,初始化程序具有签名 params(x: Integer, y: Integer).void,但对于 ThreeDCoordinate,它具有签名 params(x: Integer, y: Integer, z: Integer).void。现在这些签名彼此不兼容,所以 Sorbet 不允许您从另一个继承一个。

T::InexactStruct 允许您放弃构造函数中的强类型,并换取能够对类型结构进行继承。