I wrote some dart code today, which essentially boils down to this:
extension MapCounting<ItemT> on Map<ItemT, num> {
num get maxValue => values.reduce((max, value) => (max < value)? value : max);
}
Now, imagine I have a main()
method like this:
void main() {
Map<String, int> myMap = {
"a": 1,
"b": 2,
"c": 3,
};
var count = myMap.maxValue; // valid because Map<String, int> is a valid subtype
}
The above code doesn't throw compile-time errors. However, if you run it, you'll get this error:
TypeError: Instance of '(num, num) => num': type '(num, num) => num' is not a subtype of type '(int, int) => int'
I think I understand why it's an error -- really, the crux of it comes down to how my "reducer" function only returns num
when it needs to return int
. This is because Map<String, int>.reduce()
expects a int Function(int, int)
, and my definition can only guarantee it's a num Function(...)
.
I have two questions regarding this:
I technically found an answer to my second question, but it basically involves rewriting my code outside the extension
. Is it possible to do it with a get maxValue
getter inside the extension
?
- Why does the compiler not catch this as an error? Shouldn't it?
It compiles because:
MapCounting<ItemT>
extension is legal on its own.Map<String, int>
as a subtype of Map<String, num>
. In general, treating Generic<Derived>
as a subtype of Generic<Base>
often is convenient, but there are a number of cases where it's not actually safe.The compiler can't determine that applying a legal extension to (what it considers to be) a legal subtype won't work at runtime.
- How can I fix my extension (nicely)?
Don't use num
. Make your extension work on the actual num
subtype:
extension MapCounting<ItemT, N extends num> on Map<ItemT, N> {
N get maxValue => values.reduce((max, value) => (max < value)? value : max);
}