Skip to content

avoid_future_of_option

v1.0.0WarningConfigurablefpdart

This rule flags a function or method returning Future<Option<T>>, which TaskOption<T> already expresses.

The Option counterpart of avoid_future_of_either, and the same argument.

Future<Option<T>> is correct but throws away composition: a caller has to await before it can flatMap, so every chain leaves the fpdart world and comes back. And because a Future starts running the moment it is created, the work is already under way by the time anyone holds it — it cannot be retried, delayed, or built up now and run later.

TaskOption<T> is the type that already means “async computation that may find nothing”, and it stays lazy until .run().

Future<Option<User>> findUser(String id) async {
return Option.fromNullable(await cache.get(id));
}
TaskOption<User> findUser(String id) =>
TaskOption(() async => Option.fromNullable(await cache.get(id)));

FutureOr<Option<T>> is not reported: it may complete synchronously, so it is a different shape with a different answer.

A generator (Stream<Option<T>> with async*) is never reported — one TaskOption cannot stand in for a stream of values.

The reverse nesting, Option<Future<T>>, belongs to avoid_either_of_future, which covers both synchronous wrappers.

No quick fix is offered: the conversion changes a public signature, so every call site needs updating and an “apply all” would leave the project uncompilable.

There is an assist. Put the cursor on the function and pick “Convert to TaskOption — it rewrites the signature and moves the body into a TaskOption. See Assists.

analysis_options.yaml
many_lints:
rules:
avoid_future_of_option:
ignore_private: true
Option Type Default Description
ignore_private bool false Skip private functions and methods

This rule is in the opinionated preset. With a lower preset, enable it by name with avoid_future_of_option: true.

To turn it off:

many_lints.yaml
rules:
avoid_future_of_option: false

To keep the rule on but skip certain paths, use per-rule exclude.