Dart 无法推断子类型 class

Dart fails to infer type of sub class

我已将这段代码缩减到下面的文件中。

import 'package:hooks_riverpod/hooks_riverpod.dart';

// this class disctates what other classes do
abstract class BaseUtils<T> {
  T fromMap(Map<String, dynamic> json);
}

// Model to be used
class SampleModel extends BaseUtils {
  late int? id;

  SampleModel({
    this.id,
  });

  @override
  SampleModel fromMap(Map<String, dynamic> json) {
    return SampleModel(
      id: json['id'],
    );
  }
}

// First service
class ServiceA<T extends BaseUtils> {}

final serviceAProvider = Provider<ServiceA>((ref) {
  return ServiceA();
});

class ServiceB {
  final ServiceA<SampleModel> _service;
  ServiceB(this._service);

  void func() {
    print(_service);
  }
}

final serviceBProvider = Provider<ServiceB>((ref) {
  final _a = ref.read(serviceAProvider);
  //// Error here
  return ServiceB(_a);
});


ServiceB依赖ServiceA获取SampleModel形式的数据。 但是我得到一个 IDE 错误 The argument type 'ServiceA<BaseUtils<dynamic>>' can't be assigned to the parameter type 'ServiceA<SampleModel>' 这不应该发生

我可能做错了什么?

因为 ServiceA 是泛型 class,当你写 Provider<ServiceA> 而没有为 ServiceA 指定类型参数时,Dart 隐式地​​使用满足它的最广泛的类型类型约束。对于 ServiceA<T>,由于 T 必须扩展 BaseUtils,没有显式类型参数的 ServiceA 等价于 ServiceA<BaseUtils>,而后者又等价于 ServiceA<BaseUtils<dynamic>> 由于 BaseUtils 是一个通用的 class,对其类型参数没有限制。

错误消息是正确的:您无法安全地将 ServiceA<BaseUtils<dynamic>> 分配给 ServiceA<SampleModel>。您有一个超类型 (ServiceA<BaseUtils<dynamic>>) 和一个子类型 (ServiceA<SampleModel>)。虽然子类型是超类型,但并不是每个超类型都是子类型!

在您的 analysis_options.yaml 中启用 strict-raw-types option 应该有助于在将来发现类似的错误。

我没有使用 package:provider 的经验,但我希望解决方法是为您的泛型 classes 提供显式类型。例如:

final serviceAProvider = Provider<ServiceA>((ref) {
  return ServiceA();
})

也许应该是:

final serviceAProvider = Provider<ServiceA<SampleModel>>((ref) {
  return ServiceA<SampleModel>();
})

由于这需要为每种类型单独设置一个 serviceAProvider 变量,因此创建一个通用函数可能更简洁,以便调用者可以指定一个类型参数:

final _serviceAMap = <Type, Provider<ServiceA>>{};

Provider<ServiceA<T>> getServiceAProvider<T extends BaseUtils>() {
  var provider = _serviceAMap[T];
  if (provider != null) {
    return provider as Provider<ServiceA<T>>;
  }
  
  return _serviceAMap[T] = Provider<ServiceA<T>>((ref) => ServiceA<T>());
}

final serviceBProvider = Provider<ServiceB>((ref) {
  final _a = ref.read(getServiceAProvider<SampleModel>());
  return ServiceB(_a);
});