How to have Dart infer Widget Generic Type based on property defined as same type?

Viewed 74

Say I have the following:

class SomeObject<TModel> extends StatelessWidget {
  final TModel someModel;
  final Widget Function(TModel model) builder;
  SomeObject({required this.someModel, required this.builder});
}

and I use this like this:

return SomeObject(someModel: SomeModel(), builder: (model) => {do something});

What I'm seeing is that SomeObject doesn't get the type of TModel inferred by SomeModel() setting someModel as I would see automatically happen in C# and thus builder: (model) is Object? instead of the correct type of someModel

How does one annotate to get dart to know that someModel being set defines the overall type of TModel for all of SomeObject and thus assign the builder: (model) to the right SomeObject automatically?

1 Answers

(I am not an expert on how Dart's type inference system works, so this is an educated guess.)

Given:

var object = SomeObject(someModel: SomeModel(), builder: (model) => ...);

SomeObject's type parameter isn't specified, so it must be inferred from its arguments. Its argument types are SomeModel and Function(Object). Should SomeObject be inferred from the someModel argument to be SomeObject<SomeModel>, or should it be inferred from the builder argument to be SomeObject<Object>? Both seem equally valid.

For the builder argument to be inferred as Function(SomeModel), inference would need to flow from the someModel argument out to the construction of SomeObject and then back down to the builder argument. Dart argument type inference doesn't work that way.

Additionally, since your problematic line is specifically a return statement, I suspect that the function it's from looks like:

SomeObject someFunction() {
  return SomeObject(someModel: SomeModel(), builder: (model) => ...);
}

If so, you should be aware that the SomeObject return type without an explicit type parameter is shorthand for SomeObject<dynamic>1. Inference then will flow from the return type to the returned object, making it the equivalent of return SomeObject<dynamic>(...). To fix this, you should explicitly specify the return type to be SomeObject<SomeModel>, which should cause type inference to flow to the construction arguments and fix your problem.

Incidentally, you also should specify a return type for Function(TModel model) builder; declaration.


1 This can be caught by the Dart analyzer by setting strict-raw-types: true in analysis_options.yaml. See https://github.com/dart-lang/language/blob/master/resources/type-system/strict-raw-types.md for more details.

Related