9

这是示例代码:

# typed: true

class KeyGetter

  sig {params(env_var_name: String).returns(KeyGetter)}
  def self.from_env_var(env_var_name)
    return Null.new if env_var_name.nil?

    return new(env_var_name)
  end

  def initialize(env_var_name)
    @env_var_name = env_var_name
  end

  def to_key
    "key from #{@env_var_name}"
  end

  def to_s
    "str from #{@env_var_name}"
  end

  class Null
    def to_key; end
    def to_s; end
  end
end

在它上面运行srb tc失败

key_getter.rb:7: Returning value that does not conform to method result type https://srb.help/7005
     7 |    return Null.new if env_var_name.nil?
            ^^^^^^^^^^^^^^^
  Expected KeyGetter
    key_getter.rb:6: Method from_env_var has return type KeyGetter
     6 |  def self.from_env_var(env_var_name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  Got KeyGetter::Null originating from:
    key_getter.rb:7:
     7 |    return Null.new if env_var_name.nil?
                   ^^^^^^^^

我看到了几种解决方法:

  1. .returns(T.any(KeyGetter, KeyGetter::Null))在 sig 中使用类似的东西。
  2. 使KeyGetter::Null继承自KeyGetter.
  3. 提取一个“接口”并期待它。

    class KeyGetter
      module Interface
        def to_key; end
        def to_s; end
      end
    
      class Null
        include KeyGetter::Interface
      end
    
      include Interface
    
      sig {params(env_var_name: String).returns(KeyGetter::Interface)}
      def self.from_env_var(env_var_name)
        return Null.new if env_var_name.nil?
    
        return new(env_var_name)
      end
    

但是我想知道(并且在文档中没有找到)是:我可以描述鸭子类型吗?就像我们可以在 YARD 中做的那样,例如:

 # @returns [(#to_s, #to_key)]

或者它是一个天生有缺陷的想法(因为理想情况下我们也需要注释鸭子类型的方法。并且在这样做时不要迷失在语法中)。

所以是的,我们可以在这里对鸭子类型进行内联注释吗?如果不是,我们应该怎么做?

4

1 回答 1

2

但是我想知道(并且在文档中没有找到)是:我可以描述鸭子类型吗?就像我们可以在 YARD 中做的那样,例如:

我发现冰糕对带有特定键的散列的支持非常有限(流程称为“密封对象”)。您可以尝试这样的事情,但foo会被识别为T::Hash[T.untyped, T.untyped],或最多T::Hash[String, String]

extend T::Sig

sig { returns({to_s: String, to_key: String}) }
def foo
  T.unsafe(nil)
end

T.reveal_type(foo)
foo.to_s
foo.to_key

参见 Sorbet.run

他们尝试使用Typed Struct ( [T::Struct]) 来解决这个问题,但这与您自己定义类/接口没有什么不同。

Sorbet 确实支持元组,但这在这里也不理想。参见 Sorbet.run

或者它是一个天生有缺陷的想法(因为理想情况下我们也需要注释鸭子类型的方法。并且在这样做时不要迷失在语法中)。

既然你想对鸭子类型的方法进行注释,那就更要为它定义一个类。在您概述的方法中,我最喜欢选项 (2)。

您也可以将 NULL 设为常量值。但是考虑到当前代码的实现方式,它可能不如选项(2)

KeyGetter::NULL = KeyGetter.new(nil)
于 2019-06-23T16:30:21.383 回答