Обратный вызов с параметром универсального типа в Dart - PullRequest
2 голосов
/ 25 апреля 2019

Я пытаюсь определить функцию обратного вызова, которая должна принимать универсальный параметр и возвращать значение того же типа. Помните, что следующий пример является упрощенной версией того, что мне действительно нужно.

final T Function<T>(T value) self = (value) => value

Это приводит к следующей ошибке, от которой я не могу избавиться.

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

dart(argument_type_not_assignable)

Единственная вещь, которая, кажется, работает, - это присвоить значению тип, но это в первую очередь сводит на нет цель использования параметра типа.

final T Function<T>(T value) same = <String>(value) => value;

Мне нужно, чтобы он был универсальным, чтобы вызывающий мог передать ожидаемый тип в ответ. Мне также нужно, чтобы он был сохранен в переменной, чтобы я мог передать его как функцию обратного вызова.

Если это невозможно напрямую, знаете ли вы какие-нибудь обходные пути? Заранее спасибо.


Вот более полный пример, если требования не ясны.

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
}

Раздражает то, что Дарт действительно понимает, что foo.provide(StringProvider()) вернет строку и что использование NumberProvider действительно вернет целое число, и все же ошибка все еще возникает для строки, где переменной фактически присваивается значение .

final foo = Foo(provide: (provider) => provider.bar);
The argument type '(dynamic) → dynamic' can't be assigned to the parameter type '<T>(Provider<T>) → T'

Ответы [ 2 ]

1 голос
/ 25 апреля 2019

Оказывается, я могу обмануть проверку типов, указав любой конкретный тип при определении значения.Обратите внимание, что dynamic недопустимо, но все остальное уходит.

final foo = Foo(provide: <int>(provider) => provider.bar);

Это устраняет ошибку и позволяет методу provide возвращать правильный тип при вызове.

В заключение, это кажется простым недостатком средства проверки типов, а не тем, что на самом деле невозможно или трудно достичь с помощью уже существующих языковых функций.Я буду поднимать вопрос о репозитории языка GitHub для дальнейшего изучения и обсуждения.


Обновление # 1 : проблема была открыта на GitHub .


Обновление № 2 : проблема была решена, и выясняется, что поведение является заданным.Цитируя Эрика Эрнста из команды SDK:

Попробуйте: final foo = Foo(provide: <T>(Provider<T> provider) => provider.bar);!

Проблема в том, что вы передаете неуниверсальную функциюконструктор Foo, и вы должны передать обобщенную функцию.Между типовым типом функции и типовым типом функции нет взаимосвязи подтипов, поэтому в соответствии с средством проверки типов вы также можете передать строку, и это является причиной сообщения «не может быть назначено».

Оказывается, простое добавление <T> перед списком параметров (вместо int, как в оригинальном обходном пути) решило проблему.

final foo = Foo(provide: <T>(provider) => provider.bar);

Это заставляет Дарт понятьэтот поставщик имеет тип Provider<T>, и метод возвращает значение типа T, что избавляет нас от использования конкретного типа и по-прежнему избавляет от ошибки.

0 голосов
/ 25 апреля 2019

Вы должны определить отдельную универсальную функцию:

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

  const Foo({ this.provide });
}

main() {
  T f<T>(Provider<T> provider) => provider.bar;
  final foo = Foo(provide: f);

  String strValue = foo.provide(StringProvider()); // should be 'bar'
  int numValue = foo.provide(NumberProvider()); // should be 42
}
...