Dart 中带有泛型类型参数的回调

skr*_*orn 5 generics type-parameter dart dart-2

我正在尝试定义一个需要接受泛型参数并返回相同类型值的回调函数。请记住,以下示例是我实际需要的过度简化版本。

final T Function<T>(T value) self = (value) => value
Run Code Online (Sandbox Code Playgroud)

这导致以下错误,我似乎无法摆脱。

The argument type '(dynamic) ? dynamic' can't be assigned to the parameter type '<T>(T) ? T'

dart(argument_type_not_assignable)
Run Code Online (Sandbox Code Playgroud)

唯一似乎有效的方法是给值一个类型,但这首先违背了使用类型参数的目的。

The argument type '(dynamic) ? dynamic' can't be assigned to the parameter type '<T>(T) ? T'

dart(argument_type_not_assignable)
Run Code Online (Sandbox Code Playgroud)

我需要它是通用的,以便调用者可以传递它期望的类型作为回报。我还需要将它存储在一个变量中,以便我可以将它作为回调函数传递。

如果这不是直接可能的,您知道任何解决方法吗?先感谢您。


如果要求不明确,这里有一个更完整的示例。

final T Function<T>(T value) same = <String>(value) => value;
Run Code Online (Sandbox Code Playgroud)

令人讨厌的是,Dart 实际上知道这foo.provide(StringProvider())将返回一个字符串,而 usingNumberProvider确实会返回一个整数,但是,对于实际给变量赋值的那一行,错误仍然会增加。

abstract class Provider<T> {
  T get bar;
}

class StringProvider extends Provider<String> {
  String get bar => 'bar';
}

class NumberProvider extends Provider<int> {
  int get bar => 42;
}

class Foo {
  final T Function<T>(Provider<T> provider) provide;

  const Foo({ this.provide });
}

test() {
  final foo = Foo(provide: (provider) => provider.bar);

  String strValue = foo.provide(StringProvider()); // should be 'bar'
  int numValue = foo.provide(NumberProvider()); // should be 42
}
Run Code Online (Sandbox Code Playgroud)
The argument type '(dynamic) ? dynamic' can't be assigned to the parameter type '<T>(Provider<T>) ? T'
Run Code Online (Sandbox Code Playgroud)

skr*_*orn 5

事实证明,我可以通过在定义值时给出任何具体类型来欺骗类型检查器。请注意,这dynamic是不允许的,但其他任何事情都可以。

final foo = Foo(provide: <int>(provider) => provider.bar);
Run Code Online (Sandbox Code Playgroud)

这既消除了错误,又允许provide方法在调用时返回正确的类型。

总而言之,这似乎是类型检查器的一个简单缺点,而不是使用已经存在的语言功能实际上不可能或难以实现的。我将在该语言的 GitHub 存储库上提出一个问题,以便进一步调查和讨论。


更新 #1:该问题已在GitHub 上打开。


更新 #2:问题已解决,事实证明该行为是设计使然。引用SDK 团队的Erik Ernst的话:

试试这个:final foo = Foo(provide: <T>(Provider<T> provider) => provider.bar);

问题是你将一个非泛型函数传递给 Foo 构造函数,你应该传递一个泛型函数。泛型函数类型和非泛型函数类型之间没有子类型关系,因此根据类型检查器,您不妨传递一个字符串,这就是“无法分配给”消息的原因。

事实证明,简单地<T>在参数列表之前添加(而不是int在原始解决方法中)解决了这个问题。

final foo = Foo(provide: <T>(provider) => provider.bar);
Run Code Online (Sandbox Code Playgroud)

这迫使 Dart 理解 provider 是 typeProvider<T>并且该方法将返回一个 type 的值T,从而使我们无需使用具体类型并仍然摆脱错误。