'Flutter static members can't reference type parameters of the class

I am trying to create an abstract class with all the common functions so that code repetition is less. For e.g I have two model classes TestModel and TestModel1 with the same functionality of adding, getting data to Firestore but with different attributes mainly name and name1. Now i wanna do TestModel.get() to get all the data stored or TestModel.add(data) to add data. But the problem is Static members can't reference type parameters of the class. Now is there any workaround to the problem or some better way to achieve what I'm trynna do. Please refer to below code for the details.

abstract class Database<T> {
  /// constants
  static final testModelName = 'TestModelName';
  static final testModelName1 = 'TestModelName1';

  /// constructor
  Database(String collectionName) {
    ref =
        FirebaseFirestore.instance.collection(collectionName).withConverter<T>(
              fromFirestore: (snapshot, _) => fromJson(snapshot.data()!),
              toFirestore: (t, _) => toJson(),
            );
  }

  static late final CollectionReference<T>
      ref; // Static members can't reference type parameters of the class.

  /// json serialization
  Map<String, dynamic> toJson();

  T fromJson(Map<String, dynamic> json);

  /// helper function

  static Future<List<T>> get() async {
    // Static members can't reference type parameters of the class.
    return (await ref.get()).docs.map((e) => e.data()).toList();
  }

  static Future<void> add(T data) async {
    // Static members can't reference type parameters of the class.
    await ref.add(data);
  }
}

class TestModel extends Database<TestModel> {
  TestModel(this.id, this.name) : super(Database.testModelName);

  final String id;
  final String name;

  @override
  Map<String, dynamic> toJson() => {
        'id': id,
        'name': name,
      };

  @override
  TestModel fromJson(Map<String, dynamic> json) =>
      TestModel(json['id'], json['name']);
}

class TestModel1 extends Database<TestModel1> {
  TestModel1(this.id, this.name, this.name1) : super(Database.testModelName1);

  final String id;
  final String name;
  final String name1;

  @override
  Map<String, dynamic> toJson() => {
        'id': id,
        'name': name,
        'name1': name1,
      };

  @override
  TestModel1 fromJson(Map<String, dynamic> json) =>
      TestModel1(json['id'], json['name'], json['name1']);
}


Solution 1:[1]

Suppose you want something like:

class Foo<T> {
  // ERROR: Static members can't reference type parameters of the class.
  static final allFoosOfExactType = <T>[];
  
  Foo() {
    allFoosOfExactType.add(this);
  }
}

A workaround could be to use a Map<Type, ...> with generic global functions to access it:

class Foo<T> {
  Foo() {
    getAllFoosOfExactType<T>().add(this);
  }
}

final _allFoosMap = <Type, List<Foo<Object?>>>{};

List<Foo<T>> getAllFoosOfExactType<T>() {
  assert(T != dynamic);
  return (_allFoosMap[T] ??= <Foo<T>>[]) as List<Foo<T>>;
}

Normally using Map<Type, ...> isn't a great idea because lookups require an exact match and would not work for derived types, but that does not seem like a concern for your expected usage.

In your case, you therefore maybe could do:

Future<List<T>> get<T>() async {
  return (await getRef<T>().get()).docs.map((e) => e.data()).toList();
}

Future<void> add<T>(T data) async {
  await getRef<T>().add(data);
}

final _allRefs = <Type, CollectionReference<Object?>>{};

void setRef<T>(CollectionReference<T> ref) {
  assert(!_allRefs.containsKey(T), 'setRef<$T> already called');
  _allRefs[T] = ref;
}

CollectionReference<T> getRef<T>() {
  assert(T != dynamic);
  return _allRefs[T]! as CollectionReference<T>;
}

abstract class Database<T> {
  /// constants
  static final testModelName = 'TestModelName';
  static final testModelName1 = 'TestModelName1';

  /// constructor
  Database(String collectionName) {
    setRef(CollectionReference<T>());
  }

  /// json serialization
  Map<String, dynamic> toJson();

  T fromJson(Map<String, dynamic> json);
}

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