aeroNotAuto
aeroNotAuto

Reputation: 280

TypeError while attempting to sort a list of custom objects

I'm new to Dart and tried to get a class to implement List using the answers here, and tried to sort a list of these objects using the docs here. I deleted most of my code in an effort to post a MWE:

import 'dart:collection';

class Transaction<E> extends ListBase<E>{
    DateTime when;
    Transaction(this.when);
    List innerList = new List();
    int get length => innerList.length;
    void set length(int length){
        innerList.length = length;
    }
    void operator[]=(int index, E value){
        innerList[index] = value;
    }
    E operator [](int index) => innerList[index];
    void add(E value) => innerList.add(value);
    void addAll(Iterable<E> all) => innerList.addAll(all);
}

class Forecaster{
    var transactions;
    Forecaster(){
        this.transactions = new List<dynamic>();
    }
    void tabulate(){
        transactions.sort((a,b) => a.when.compareTo(b.when)); //problem line?
        for(var d in transactions){
            d.asMap().forEach((index,content){
                int len = content.toStringAsFixed(2).length;
            });
        }
    }
    void forecast(var forWhen){
        var first = new Transaction(DateTime.now());
        first.addAll([5,9]);
        transactions.add(first);
    }
}

void main(){
    Forecaster myTest = new Forecaster();
    var dub = myTest;
    dub..forecast(DateTime.now())
       ..tabulate();
}

Running with the problem line results in an exception (Uncaught exception: TypeError: Closure 'Forecaster_tabulate_closure': type '(dynamic, dynamic) => dynamic' is not a subtype of type '(dynamic, dynamic) => int') I don't understand. If I comment out the problem line, the TypeError goes away. Is the TypeError because I did something wrong when defining Transaction? I'm attempting this with DartPad.

Upvotes: 0

Views: 103

Answers (2)

lrn
lrn

Reputation: 71793

Your problem is with the types. The code:

var transactions;
Forecaster(){
    this.transactions = new List<dynamic>();
}
void tabulate(){
    transactions.sort((a,b) => a.when.compareTo(b.when));

first declares transactions to have type dynamic. Then you call sort on that with an argument which is inferred to have type dynamic Function(dynamic, dynamic) (because there is no clue available to say otherwise in the type of transactions).

However, the actual run-time type of transactions is List<Transaction>, and that requires a function argument of type int Function(Transaction, Transaction). The type dynamic Function(dynamic, dynamic) is not a sub-type of int Function(Transaction, Transaction) (the return type has to be a subtype of int for that to be the case) so you get a run-time error.

If you change transactions to have type List<Transaction>, then the type inference will have a clue when it gets to the function literal. It will infer that (a, b) => a.when.compareTo(b.when) in a context expecting int Function(Transaction, Transaction) will have that type.

Even if you just change transactions to List<dynamic>, it will still work, it will just make a.when.compareTo(b.when) be dynamic invocations.

Upvotes: 1

Joshua T
Joshua T

Reputation: 2599

I'm new to Dart too, so my explanation might not be 100% on the money, but I believe, yes, the main issue is the type assignment of transactions. I think because you initialize it as a var, it is having trouble deducing the type of a.when, which is means it also doesn't know the type of a.when.compareTo(), and assumes dynamic. You are feeding the output of compareTo into List.sort() which is expecting an int from the anonymous function. Thus the error that it wanted an int but got dynamic.

The easiest way to address this is to initialize transactions with a more explicit type rather than as var:

List<Transaction> transactions;
Forecaster(){
    this.transactions = new List<Transaction>();
}

Also, to confirm that it is an issue with it not being able to infer the return type of compareTo, I tried leaving your code as-is, but explicitly casting the result as int, and that also worked:

transactions.sort((a,b){
    return (a.when.compareTo(b.when) as int);
});

Note: code like the above and using lots of dynamics and vars is in general not great practice with Dart - you lose a lot of the benefits of having a typed language. You might also notice that when you type in an IDE, you don't get methods auto-suggested when you do stuff like this - for example, until I changed transactions to an explicit type of list, typing a.when did not trigger autocomplete, and my IDE thought the type was dynamic, not DateTime.

Upvotes: 2

Related Questions