下面的示例代码会导致srb
错误,并带有注解:参数的类型必须是与超方法上的参数类型相同的超类型。
editor.rb:29: Block parameter _block of type T.proc.params(arg0: T.class_of(Admin::PostAuthAction::PostActionContext)).returns(Admin::PostAuthAction::PostActionContext) not compatible with type of abstract method Lifecycle::PostAuthAction#sync https://srb.help/5035
29 | def sync(&_block)
^^^^^^^^^^^^^^^^^
editor.rb:11: The super method parameter _block was declared here with type T.proc.params(arg0: T.class_of(T::Struct)).returns(T::Struct)
11 | def sync(&_block); end
^^^^^^^^^^^^^^^^^
Note:
A parameter's type must be a supertype of the same parameter's type on the super method.
# typed: strict
module Lifecycle
module PostAuthAction
extend T::Sig
extend T::Helpers
interface!
sig do
abstract.params(
_block: T.proc.params(arg0: T.class_of(T::Struct)).returns(T::Struct)
).void
end
def sync(&_block); end
end
end
module Admin
class PostAuthAction
include Lifecycle::PostAuthAction
extend T::Sig
class PostActionContext < T::Struct
const :user, Object
end
PostActionContextCallback = T.type_alias do
T.proc.params(arg0: T.class_of(PostActionContext)).returns(PostActionContext)
end
sig { override.params(_block: PostActionContextCallback).void }
def sync(&_block)
context = yield(PostActionContext)
end
end
end
我的期望是接口应该定义上限,其中接口的签名期望一个接受类T::Struct
并返回T::Struct
示例的块。
该实现提供了T::Struct
的子类,并导致了此键入错误。相反,接口定义了继承的下限,我只能提供T::Struct
的祖先,而不是后代。
怎么了?
如果我添加泛型,我就能够完成预期的LSP(Liskov替换)。下面是上面代码的一个plaground,以及一个使用泛型的解决方案
1条答案
按热度按时间xn1cxnb41#
Sorbet对原始实现的抱怨是正确的:子类不能重写一个方法来接收比父类更具体的类型。
要了解为什么这是一个问题,请看下面的示例:
关键是遵循Robustness principle:“保守你所发送的,自由你所接受的”。当处理冰糕签名时,这意味着:
params
声明为类型,或者比父签名中的类型更一般(父)。returns
only 声明为类型,或者比父签名中的类型更具体的类型(子)。