当代码流已经知道 value 不为 nil 时,如何避免使用 Sorbet 的 T.must ?

Vin*_*cha 1 ruby ruby-on-rails sorbet

我在 Rails 项目上使用 Sorbet,并且有一个方法可以对 nilable 属性进行计算。

def age
  return unless dob && dob.year > 1900

  now = Time.now.utc.to_date
  now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end
Run Code Online (Sandbox Code Playgroud)

在该代码上,dob是nilable。如果我对它进行类型检查,我会收到一堆抱怨,要求将其包装dob在里面T.must(dob)

app/models/person.rb:18: Method year does not exist on NilClass component of T.nilable(Date) https://srb.help/7003
    18 |    return unless dob && dob.year > 1900
                                     ^^^^
  Got T.nilable(Date) originating from:
    app/models/person.rb:18:
    18 |    return unless dob && dob.year > 1900
                                 ^^^
  Autocorrect: Use `-a` to autocorrect
    app/models/person.rb:18: Replace with T.must(dob)
    18 |    return unless dob && dob.year > 1900
                                 ^^^

app/models/person.rb:21: Method year does not exist on NilClass component of T.nilable(Date) https://srb.help/7003
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                           ^^^^
  Got T.nilable(Date) originating from:
    app/models/person.rb:21:
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                       ^^^
  Autocorrect: Use `-a` to autocorrect
    app/models/person.rb:21: Replace with T.must(dob)
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                       ^^^

app/models/person.rb:21: Method month does not exist on NilClass component of T.nilable(Date) https://srb.help/7003
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                                                    ^^^^^
  Got T.nilable(Date) originating from:
    app/models/person.rb:21:
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                                                ^^^
  Autocorrect: Use `-a` to autocorrect
    app/models/person.rb:21: Replace with T.must(dob)
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                                                ^^^

app/models/person.rb:21: Method month does not exist on NilClass component of T.nilable(Date) https://srb.help/7003
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                                                                               ^^^^^
  Got T.nilable(Date) originating from:
    app/models/person.rb:21:
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                                                                           ^^^
  Autocorrect: Use `-a` to autocorrect
    app/models/person.rb:21: Replace with T.must(dob)
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                                                                           ^^^

app/models/person.rb:21: Method day does not exist on NilClass component of T.nilable(Date) https://srb.help/7003
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                                                                                                       ^^^
  Got T.nilable(Date) originating from:
    app/models/person.rb:21:
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
                                                                                                   ^^^
  Autocorrect: Use `-a` to autocorrect
    app/models/person.rb:21: Replace with T.must(dob)
    21 |    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
Run Code Online (Sandbox Code Playgroud)

告诉冰糕这dob不会是零的最好方法是什么,因为如果是的话我已经回来了。

jez*_*jez 6

这是 Sorbet 最常见的问题,它出现在Sorbet 常见问题解答页面的顶部。

\n

简短的答案是,您需要分配dob给一个变量(尽管没有任何括号,但dob实际上是对此上下文中的方法的调用,因为在此范围内尚未对调用的变量进行分配dob)。

\n

dob = self.dob对于您的情况,最简单的解决方法是在方法的顶部编写,该dob方法调用该方法一次并将其分配给一个变量:

\n
def age\n  dob = self.dob\n  return unless dob && dob.year > 1900\n\n  now = Time.now.utc.to_date\n  now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)\nend\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x86\x92 查看 sorbet.run 上的完整示例

\n

第一次赋值后的所有引用dob现在都是从局部变量读取,而不是调用方法,这允许 Sorbet 记住它执行的任何流敏感类型分析。

\n