dart

type '(int) => void' is not a subtype of type '(Object) => void'


I checked all my generics are all OK. Inheritance seems ok. Type inference seems ok. No compiler error. I don't have any hard type casting. Everything is seems strongly typed.

class ModelField {}

class DropdownField<V> extends ModelField {
  final void Function(V value) setValue;

  final List<V> options;

  DropdownField({
    required this.setValue,
    required this.options,
  });
}

void _buildDropdownField<V>(DropdownField<V> field) {
  final options = field.options;
  final data = options.first;

  field.setValue(data); // error Here
}

void _renderField(ModelField field) {
  /// from server
  // if field is StringField, else if field is DateField, ... else
  if (field is DropdownField<Object>) {
    // print('field is DropdownField<dynamic, dynamic>');
    _buildDropdownField(field);
  }
}

void main() {
  final field =
      DropdownField<int>(setValue: (value) => print(value), options: [1, 2, 3]);

  _renderField(field);
}

But when I run:

Unhandled exception:
type '(int) => void' is not a subtype of type '(Object) => void'
#0      _buildDropdownField (package:shared_core/weird_dart/generic_promotion.dart:18:9)
#1      _renderField (package:shared_core/weird_dart/generic_promotion.dart:26:5)
#2      main (package:shared_core/weird_dart/generic_promotion.dart:34:3)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

How can int not be a subtype of Object???

I am so confused (or Dart is) I hope it's me. But this error occurs only at run time.


Solution

  • The proper solution I found is to give the compiler "more clues" about the Sound Variance. Hence use the keyword inout Thus: class DropdownField<inout V> . What the inout keyword means

    “Hey, compiler, V in DropdownField is gonna both take values in and give them out. V is in when we pass it to setValue, and it’s out when we get values from options. So don’t stress—it’s all good for both directions!”

    Note:

    This still an experimental flag.

    environment:
      sdk: '>=3.4.3 <4.0.0'
    

    And add the experimental flag. --enable-experiment=variance

    class ModelField {}
    
    class DropdownField<inout V> extends ModelField {
      final void Function(V value) setValue;
    
      final List<V> options;
    
      DropdownField({
        required this.setValue,
        required this.options,
      });
    }
    
    void _buildDropdownField<V>(DropdownField<V> field) {
      final options = field.options;
      final data = options.first;
    
      print(field.setValue);
      field.setValue(data);
    }
    
    void _renderField(ModelField field) {
      /// from server
      // if field is StringField, else if field is DateField, ... else
      if (field is DropdownField<Object>) {
        // print('field is DropdownField<dynamic, dynamic>');
        _buildDropdownField(field);
      }
    }
    
    void main() {
      final field =
      DropdownField<int>(setValue: (value) => print(value), options: [1, 2, 3]);
    
      _renderField(field);
    }