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);
});
我已将这段代码缩减到下面的文件中。
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);
});