[get_it] Get all registered types in the current scope

@escamoteur I’m in a scenario where I need to get a list of all GetIt registrations, so I can stop feeding another library with registrations and automate it.

I’ve copied the _GetItImplementation and added this method:

List<Type> getRegisteredTypes() {
    _Scope registrationScope;
    int i = _scopes.length;
    // find the first not final scope
    do {
      i--;
      registrationScope = _scopes[i];
    } while (registrationScope.isFinal && i >= 0);
    assert(
      i >= 0,
      'The baseScope should always be open. If you see this error please file an issue at',
    );

    return registrationScope.typeRegistrations.keys.toList();
  }

It works, but I don’t know if this is correct (especially scope-wise).

Also, it would be awesome if you could add such capability in the library.

Could you explain a bit what you are doing with this?

Before I add a new function to a package I like to understand the application a bit better because maybe I can offer another solution or a better approach that might be used by others too.

Thanks, but I think it is impossible to achieve what I want anyway =(

See, I’m using a library where I need to map one class to another (a Query class to a Query Handler class, a Command class to a Command Handler class, etc.). It’s very tedious to do it by hand:

  final queryHandlers = <Type, IQueryHandler<IQuery<dynamic>, dynamic> Function()>{
    AppVersionQuery: AppVersionQueryHandler.new,
    DeviceInfoQuery:
        () => const DeviceInfoQueryHandler(deviceInfoService: deviceInfoService),
    UserSettingValueQuery:
        () => UserSettingValueQueryHandler(
          authenticationService: authenticationService,
          databaseService: databaseService,
        ),
    CurrentThemeQuery:
        () => CurrentThemeQueryHandler(
          authenticationService: authenticationService,
          databaseService: databaseService,
          defaultUserTheme: defaultThemeSetting,
        ),
    UserCredentialsQuery:
        () => UserCredentialsQueryHandler(
          authenticationService: authenticationService,
        ),
    CurrentUserRefQuery:
        () =>
            CurrentUserRefQueryHandler(authenticationService: authenticationService),
  };

Then, I wondered: what If I use the injectable | Dart package to automagically register all my classes without me having to do this manual setup (which is the same for pure GetIt as well, that’s why Injector exists in the first place).

So, with all classes registered, I could somehow list and search of all registrations that starts with IQueryHandler< and, somehow, create that setup above.

But Dart makes that impossible:

  1. There is no way to create a type from a String (like C# Type.GetType("SomeCommand")).

  2. There is no guarantee whatsoever that "${SomeCommand}" == "SomeCommand", because Dart compiler may obfuscate/compact type names (which is not a problem, since all strings will match, they would just not have the same value (i.e.: instead of SomeCommand, could be $_obf234, but it points to the same type)).

So, in the end, I did write a modified version of GetIt to get the list of registered types, but I was unable to actually construct any types from it.

The library I use convert types to strings and keep a map of type name : handler factory:

Future<List<TResult>> _dispatch(
    TAction action, [
    bool ignoreHandlers = false,
  ]) async {
    final typeName = action.runtimeType.toString();
    final handlerFactories = _handlerFactories[typeName];

So, when calling this as _dispatch(const AppVersionQuery()), AppVersionQueryHandler is constructed and the .handler(AppVersionQuery query) is executed.

You could just have used the optional registration name in get_it :blush: and register them as factories.
Although I still wonder why you have to map them at all.
I tend to wrap such things in proxy classes recently which allows some additional flexibility.

The same reason we have to register all dependencies in GetIt (at its core, it’s something like a Map<Type, FactoryThatReturnsType>). Same for this library (it’s a Map<Message, FactoryForMessageHandling>).

And, yes, it’s annoying. Hence, the (amazing) Injectable.