'How to prevent Function cast Exceptions in Dart

I couldn't figure out how to store functions while keeping the storage agnostic about the functions' parameter type. I try to implement a json registry used for encoding/decoding. Decoding works fine but when it comes to encoding I get this error on runtime

E/flutter ( 4637): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: type '(QuotationRequest) => Map<String, dynamic>' is not a subtype of type '(Object) => Map<String, dynamic>'
E/flutter ( 4637): #0      JsonifyRegistry.encode (package:ridecube/src/commons/io/jsonify.dart:31:21)
...

when I call this:

final registry = JsonifyRegistry()
        ..register<QuotationRequest>(encoder: (_) => _.toJson(), decoder: (_) => QuotationRequest.fromJson(_));
final QuotationRequest data = QuotationRequest();
registry.encode(data); // <-- issue here

Hence, I'm curious how to do proper casting when it comes to dart function objects if possible.

// jsonify.dart
typedef Json = Map<String, dynamic>;
typedef JsonifyEncoder<T> = Json Function(T object);
typedef JsonifyDecoder<T> = T Function(Json encoded);

class JsonifyRegistryEntry<T> {
  JsonifyRegistryEntry({required this.encoder, required this.decoder});

  final JsonifyEncoder<T> encoder;
  final JsonifyDecoder<T> decoder;
}

class JsonifyRegistry {

  final Map<Type, JsonifyRegistryEntry<Object>> _reg = {};

  void register<T>({required JsonifyEncoder<T> encoder, required JsonifyDecoder<T> decoder}) =>
      registerEntry(JsonifyRegistryEntry<T>(encoder: encoder, decoder: decoder));

  void registerEntry<T>(JsonifyRegistryEntry<T> entry) {
    _reg[T] = entry as JsonifyRegistryEntry<Object>;
  }

  T decode<T>(Json encoded) {
    assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
    return _reg[T]!.decoder(encoded) as T;
  }

  Json encode<T>(T object) {
    assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
    return _reg[T]!.encoder(object!); //line 31
  }
}

Note

I know why this is prevented of course but I'm still trying to find a solution for my implementation.

simple explanation:

typedef Fu1 = int Function(Object);
typedef Fu2 = int Function(int);

void main() {
  final Fu2 fu2 = (x) => x;
  final Fu1 fu1 = fu2 as Fu1; // error here Closure 'main_closure': type '(int) => int' is not a subtype of type '(Object) => int'
  // if this wasn't the case you would get
  // an error as soon as you make a call like
  // this: fu1('string');
}


Solution 1:[1]

this is one possible solution but I don't like that I had to replace the property encoder by a method.. Maybe you guys can come up with a better advice.

class JsonifyRegistryEntry<T> {
  JsonifyRegistryEntry({required JsonifyEncoder<T> encoder, required this.decoder}): _encoder = encoder;

  final Function _encoder;
  JsonifyEncoder<E> encoder<E>() => _encoder as JsonifyEncoder<E>;
  final JsonifyDecoder<T> decoder;
}
// in JsonifyRegistry:

  Json encode<T>(T object) {
    assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
    return _reg[T]!.encoder<T>()(object!);
  }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Osman