Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dart 3.3 interface field type promotion not working

Tags:

types

dart

I don't understand why the following example doesn't work:

void main() {
  final BaseClass something = ConcreteClass('test');

  if (something is I) {
    print(something.exists); // The getter 'exists' isn't defined for the type 'BaseClass'.
  }
}

abstract interface class I {
  String get exists;
}

abstract class BaseClass {}

class ConcreteClass extends BaseClass implements I {
  ConcreteClass(this.exists);

  @override
  final String exists;
}

Am I hitting the "getter" limitation in type promotion mentioned here: https://dart.dev/tools/non-promotion-reasons#not-field ?

If so, how can I solve this without resorting to casting (which I try to avoid)? Adding exists to BaseClass is not an option in my real code.

Also, the release notes for Dart 3.3 says:

Abstract getters are now promotable under the rules of private final field promotion, if there are no conflicting declarations.

... which sounds like what I want?

like image 572
DarkNeuron Avatar asked Oct 20 '25 11:10

DarkNeuron


1 Answers

The problem you are hitting are described here:
https://dart.dev/tools/non-promotion-reasons#subtype-mismatch

In short, the problem is that you are trying to do type promotion which would remove functionality from the something variable. In this case, if you promote the type to I, the type of the something variable would become I but then, it is no longer a BaseClass. So since Dart does not have the concept of multiple types on the same variable, it can't say something are both a BaseClass and an I.

A solution would be to do a typecast:

void main() {
  final BaseClass something = ConcreteClass('test');

  if (something is I) {
    print((something as I).exists);
  }
}

But since you want to avoid this, you can also declare a new variable. This is actually rather simple if we make use of a pattern:

void main() {
  final BaseClass something = ConcreteClass('test');

  if (something case I something) {
    print(something.exists);
  }
}

Here, we declare a new local variable for the if-block called something if the method variable something are I. Since this local something would be of the type I, it would not allow calling any method from the BaseClass class.

If you need to be able to make use of functionality of both I and BaseClass, I recommend using a different name of the variable in the pattern.

For more detailed information and solutions, read the link I mentioned in the beginning.

like image 55
julemand101 Avatar answered Oct 23 '25 01:10

julemand101