'How to type the extension in Dart

I have classes called bird and dog:

class Bird extends Animal with FlyAbility, JumpAbility {
 // ...
}
class Dog extends Animal with RunAbility, JumpAbility {
 // ...
}

Both extend abstract class Animal and use mixin for their own ability.

Now I like to use some extensions on it to output their information:

extension BirdSheetExporter on Bird {
  RowData export() {
    return RowData(values: [
      // type
      CellData(stringValue: 'bird'),
      // name
      CellData(stringValue: name),
      // ...
    ]);
  }
}

extension DogSheetExporter on Dog {
  RowData export() {
    return RowData(values: [
      // type
      CellData(stringValue: 'dog'),
      // name
      CellData(stringValue: name),
      // ...
    ]);
  }
}

Their are also other exporter like RawTextExporter:

extension BirdRawTextExporter on Bird {
  String export() {
    String flyAbility = canFly ? 'can' : "can't";
    return 'The bird named $name $flyAbility fly';
  }
}

This is user responsibility to choose what extension they want. My question is how to give some interface for these extensions and type it?

Something like:

// extension file: raw_text_exporters.dart
abstract class RawTextExporter {
  String export();
}

// I know this is not allowed
extension BirdRawTextExporter implements RawTextExporter on Bird {
  // ..
}

// client file: export_animal_raw_text.dart
void exportAnimal(RawTextExporter animal) {
  print(animal.export());
}

Or any suggestion for this situation? I have found some work around:

typedef rawTextExporter = String Function();

void exportAnimalByExporter(rawTextExporter exporter) {
  print(exporter());
}

The reason I'm not using dynamic is code analyzer will consider the imported file not used.

import 'extensions/raw_text_exporters.dart';

// Analyzer thinks `raw_text_exporters.dart` has nothing to do with this function
void exportAnimal(dynamin animal) {
  print(animal.export());
}
// type from `raw_text_exporters.dart` and it will keeps the file
void exportAnimal(RawTextExporter animal) {
  print(animal.export());
}


Solution 1:[1]

I think the better solution will be dependency injection:

class Bird extends Animal with FlyAbility, JumpAbility, Exportable {
  // ...
}

mixin Exportable {
  T export<T>(Exporter exporter) {
    return exporter.export(bird);
  }
}

And the exporter's interface will be like:

abstract class Exporter<T> {
  T export(Animal animal);
}

class RawTextExporter<String> {
  @override
  String export(Animal animal) {
    final species = animal is Bird ? 'bird' : 'unknown';
    return 'The $species named $name ...';
  }
}

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 呂學洲