Dart Generics - type is not a subtype
Asked Answered
P

1

10

I'm getting a runtime error from a Dart generic function:

 Widget card = widget.cardBuilder(item);

Which generates:

 type '(Contact) => Widget' is not a subtype of type '(DeletableItem) => Widget'

The Contact class is defined as

class Contact
implements DeletableItem
{

I then have a method:

 class UndoableListView<T extends DeletableItem>
 {
      List<T> children;
      final Widget Function(T item) cardBuilder;

And this is where the runtime error occurs

 Widget buildItem(BuildContext context, int index) {
 T item = children[index];
 Widget card = widget.cardBuilder(item);   <<<<< error thrown here.

I'm clearly mis-understanding something with how generics work.

Contact clearly extends DeleteableItem.

So what have I done wrong?

Pickup answered 1/8, 2019 at 3:49 Comment(4)
The issue is not that Contact doesn't implement DeletableItem, but rather that the whole function itself has a different signature since (Contact) => Widget cannot be cast to (DeletableItem) => Widget. Without seeing more code, it's hard to propose a fix. Can you show the context in which buildItem is being used?Zuber
The code is extracted from a rather large app. The intent is to pass in a list of Deleteable item to a list and have the list callback the builder to generate a card from the item. I'm confused as to way it can't be cast given that the Contact is of type Deleteable?Pickup
Any updates on this? I am getting the same error, I dont understand how Contract is not a subtype of DeletablItem even tho, well, it is!Weisman
Nothing here says that Contact is not a subtype of DeletableItem, but that the function type Widget Function(Contact) is not a subtype of the function type Widget Function(DeletableItem). Function types are contravariant in their parameter types (a function type can only a subtype of another function type if the parameter types of the first function are supertypes of the parameter types of the second function). So, this is working as intended.Seligmann
S
1

Contact extends DeletableItem, and is a subtype of it.

Functions are contravariant in their parameters, so

Widget Function(Contact)

is not a subtype of

Widget Function(DeletableItem)

In fact, it's the other way around.

Functions act that way because subtyping means substitutability. You can use a Contact anywhere a DeletableItem is needed (because it implements the entire DeletableItem interface), so Contact is a subtype of DeletableItem.

You can use a Widget Function(DeletableItem) anywhere a Widget Function(Contact) is needed, because you can call that function with any Contact (and it returns a Widget as expected). So Widget Function(DeletableItem) is a subtype of Widget Function(Contact).

Dart generics are covariant, even though it's not always sound.

That means that a UndoableListView<Contact> is a subtype of UndoableListView<DeletableItem>.

The problem comes up because the cardBuilder field uses the class type parameter contravariantly in the function type,

      final Widget Function(T item) cardBuilder;

So, when you do:

UndoableListView<DeletableItem> list = UndoableListView<Contact>();
var builder = list.cardBuilder;

you get an error. The list.cardBuilder expression has a static type of Widget Function(DeletableItem), but a run-time type of Widget Function(Contact), which is not a subtype of the static type. That's a type soundness problem, one the compiler is aware of, and it inserts a type check to ensure that the builder variable won't end up with an invalidly typed value.

And that check throws.

Seligmann answered 25/4, 2022 at 6:52 Comment(1)
How to fix this error?Singhal

© 2022 - 2024 — McMap. All rights reserved.