对于类似情况,类型推断的行为不同

Type inference behaves differently for similar cases

运行 下面的代码(Dart 2.3)抛出异常: type 'List<dynamic>' is not a subtype of type 'List<bool>'

bar() => 0;
foo() => [bar()];

main() {
  var l = [1, 2, 3];
  l = foo();
}

然而,这个稍作改动的示例运行正确:

main() {
  bar() => 0;
  var l = [1, 2, 3];
  l = [bar()];
}

这样做:

main() {
  bar() => 0;
  foo() => [bar()];
  var l = [1, 2, 3];
  l = foo();
}

使这些情况表现不同的 Dart 类型推断算法是什么?似乎 foobar 函数的类型应该很容易推断出来,因为它们总是 return 相同的值。我也不清楚为什么在这些情况下移动函数声明的位置会改变类型推断。

有人知道这是怎么回事吗?

看起来嵌套函数的处理方式与 top-level 函数不同。 这可能是一个错误。 我在 Dart 2.3.1 上从 Dartpad 得到以下信息。

foo() => 0;
bar() => [foo()];

main() {
  baz() => 0;
  qux() => [baz()];
  print(foo.runtimeType);
  print(bar.runtimeType);
  print(baz.runtimeType);
  print(qux.runtimeType);
}

// () => dynamic
// () => dynamic
// () => int
// () => List<int>

解释here

This is expected behavior. Local functions use type inference to deduce their return type, but top-level/class-level functions do not.

The primary reason for the distinction is that top-level and class level functions exist at the same level as type declarations. Solving cyclic dependencies between types and functions gets even harder if we have to also analyze function bodies at a time where we don't even know the signature of classes yet.

When top-level inference has completed, we do know the type hierarchies, and where top-level functions are unordered, they can refer to each other in arbitrary ways, local functions are linear and can only depend on global functions or prior local functions. That means that we can analyze the function body locally to find the return type, without needing to look at anything except the body itself, and things we have already analyzed.

Leaf Petersen 在对 dart-lang/sdk issue #33137: Type inference of function return value 的评论中对此进行了解释:

This is by design. We do infer return types of non-recursive local functions (functions declared inside of the scope of another function or method), but for top level functions and methods, we do not infer return types (except via override inference). The reasons are as follows:

  • Methods and top level functions are usually part of the API of a program, and it's valuable to be able to quickly read off the API of a piece of code. Doing method body based return type inference means that understanding the signature of the API requires reading through the method body.
  • Methods and top level functions can be arbitrarily mutually recursive, which makes the inference problem much harder and more expensive.

For primarily these reasons, we do not infer return types for top level functions and methods. Leaving off the return type is just another way of saying dynamic.

如果你设置

analyzer:
  strong-mode:
    implicit-dynamic: false

在您的 analysis_options.yaml 文件中,然后 dartanalyzer 将在 top-level 函数具有隐式 dynamic return 类型时生成错误:

  error • Missing return type for 'bar' at example.dart:1:1 • strong_mode_implicit_dynamic_return
  error • Missing return type for 'foo' at example.dart:2:1 • strong_mode_implicit_dynamic_return