I have the following:
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
What I want is to have my ThreeDCoordinate
inherit x
and y
of Coordinate
so I don't have to rewrite them in ThreeDCoordinate
. How can I accomplish this?
There is a way to do this, using T::InexactStruct
, but you will have to give up being able to strongly type the initializer of your structs:
# 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
The problem with T::Struct
and subclassing is that Sorbet creates an initializer for your struct that takes into account all of its declared fields. So for Coordinate
the initializer has the signature params(x: Integer, y: Integer).void
but for ThreeDCoordinate
it has the signature params(x: Integer, y: Integer, z: Integer).void
. Now these signatures are not compatible with each other, so Sorbet does not allow you to subclass one from the other.
T::InexactStruct
allows you to give up the strong typing in the constructor and trade it in for being able to do inheritance on typed structs.