Можно ли указать тип утки в сигнатурах методов? - PullRequest
8 голосов
/ 21 июня 2019

Вот пример кода:

# 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)) в сигн.
  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)]

Или это изначально ошибочная идея (потому что в идеале нам нужно также аннотировать методы типа утки. И при этом не теряться в синтаксисе).

Так что да, можем ли мы аннотировать здесь тип утки?Если нет, что мы должны делать вместо этого?

1 Ответ

2 голосов
/ 23 июня 2019

Но что я хотел бы знать (и не нашел в документах): могу ли я описать тип утки?Как мы можем сделать в 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

Они пытаются решить эту проблему с помощью Типизированная структура ([T::Struct]), но этоне отличайтесь от того, как вы определяете класс / интерфейс самостоятельно.

Sorbet поддерживает кортеж, но и здесь это не было бы идеально. Смотрите на Sorbet.run

Или это изначально ошибочная идея (потому что в идеале нам нужно также аннотировать методы типа утки. И при этом не теряться в синтаксисе).

Учитывая, что вы хотите аннотировать методы типа утки, тем более определить класс для него.Мне нравится вариант (2), лучший из предложенных вами подходов.

Вместо этого вы также можете сделать NULL постоянным значением.Но, учитывая, как реализован текущий код, он, вероятно, не так хорош, как option (2)

KeyGetter::NULL = KeyGetter.new(nil)
...