avoid-types-on-closure-parameters

Same as avoid_types_on_closure_parameters in Dart lints

Group
Style
Path
lint/style/avoid-types-on-closure-parameters
Default
Off, opt in
Origin
Dart lints
Rule
150 of 512

Flags type annotations on closure parameters whose types come from context.

When a function expression is passed where a function type is expected, its parameter types are inferred from that type, so annotating them is redundant. Closures without a known function-typed context are left alone because their annotations carry information. Like the official lint, a closure that is an operand, an await operand, a switch expression arm, a record literal field, or a direct collection literal element/key/value (including collection if/for) has no context here. This does not suppress independently contextualized callbacks nested inside those expressions. Parenthesizing an operand (including the fallback of ??) does not add context. A returned closure has context only in an async body returning Future<F> for a function type F. A conditional branch or assignment RHS takes its enclosing expression's own static type as its context, including function types obtained by joining variant signatures. Joins retain the shared optional positional prefix when required arity agrees. Different required arities join to general Function, unless a concrete downward function context provides a shared signature.

names.map((String name) => name.length); // flagged
names.map((name) => name.length);        // ok

Invalid

example.dartdart
int apply(int Function(int) fn) => fn(1);

void each(List<String> names, void Function(String name) action) {}

void entries(void Function(String key, int value) action) {}

void typed(void Function(void Function() inner) action) {}

void f(List<String> names) {
  apply((int x) => x + 1);
  each(names, (String name) {
    print(name);
  });
  entries((String key, int value) {});
  entries((key, int value) {});
  typed((void inner()) {});
  final int Function(int) inc = (int x) => x + 1;
  print(inc);
}

// An `async` body returning `Future<F>` gives a returned closure context `F`.
Future<int Function(int)> later() async {
  return (int x) => x;
}

final Future<int Function(int)> Function() deferred = () async {
  return (int x) => x;
};

void sink(Object value) {}

// A conditional branch's context is the conditional's own static type.
void conditional(bool b) {
  sink(b ? (int x) => x : (int x) => -x);
}

class CallbackBox {
  const CallbackBox.named(this.callback);
  final void Function(int) callback;
}

void contravariantAndFieldFormal() {
  // The annotation need not equal the context's parameter type.
  new CallbackBox.named((Object value) {});
}
Remove the unnecessary type annotation from this function expression parameter.
9void f(List<String> names) {
10 apply((int x) => x + 1);
Remove the unnecessary type annotation from this function expression parameter.
10 apply((int x) => x + 1);
11 each(names, (String name) {
Remove the unnecessary type annotation from this function expression parameter.
13 });
14 entries((String key, int value) {});
Remove the unnecessary type annotation from this function expression parameter.
13 });
14 entries((String key, int value) {});
Remove the unnecessary type annotation from this function expression parameter.
14 entries((String key, int value) {});
15 entries((key, int value) {});
Remove the unnecessary type annotation from this function expression parameter.
15 entries((key, int value) {});
16 typed((void inner()) {});
Remove the unnecessary type annotation from this function expression parameter.
16 typed((void inner()) {});
17 final int Function(int) inc = (int x) => x + 1;
Remove the unnecessary type annotation from this function expression parameter.
22Future<int Function(int)> later() async {
23 return (int x) => x;
Remove the unnecessary type annotation from this function expression parameter.
26final Future<int Function(int)> Function() deferred = () async {
27 return (int x) => x;
Remove the unnecessary type annotation from this function expression parameter.
33void conditional(bool b) {
34 sink(b ? (int x) => x : (int x) => -x);
Remove the unnecessary type annotation from this function expression parameter.
33void conditional(bool b) {
34 sink(b ? (int x) => x : (int x) => -x);
Remove the unnecessary type annotation from this function expression parameter.
43 // The annotation need not equal the context's parameter type.
44 new CallbackBox.named((Object value) {});

Valid

example.dartdart
import 'dart:async';

int apply(int Function(int) fn) => fn(1);

void each(List<String> names, void Function(String name) action) {}

void dyn(void Function(dynamic value) action) {}

void untypedContext(Function action) {}

void f(List<String> names) {
  apply((x) => x + 1);
  each(names, (name) {
    print(name);
  });
  dyn((dynamic value) {});
  untypedContext((int x) => x);
  final g = (int x) => x;
  var h = (String s) {};
  print([g, h]);
}

// The official context approximation does not reach returned values, unless
// an `async` body returns `Future<F>`, nor operands or switch expression arms.
int Function(int) returned() {
  return (int x) => x;
}

FutureOr<int Function(int)> eventually() async {
  return (int x) => x;
}

void operand(List<String> names, int Function(String a, String b)? compare) {
  names.sort(compare ?? (String a, String b) => a.compareTo(b));
}

void arms(bool flag, List<int> values) {
  values.where(switch (flag) {
    true => (int value) => value > 0,
    false => (int value) => value < 0,
  });
}

// Nor record literal fields, even under a record context type.
void fields(List<(int, void Function(String))> entries) {
  entries.add((1, (String s) {}));
  final (int, {void Function(String) named}) record = (2, named: (String s) {});
  print(record);
}

typedef Any = dynamic;

void aliasContext(void Function(dynamic) callback) {}

void dynamicAlias() {
  aliasContext((Any value) {});
}

class Sequence<T extends Object> {
  const Sequence.single(T value);
}

void consumeSequences(List<Sequence> values) {}

void downwardConstructorContext() {
  // The raw contextual Sequence fixes T to Object, not the closure's type.
  consumeSequences([Sequence.single((Object value) {})]);
}

Configure avoid-types-on-closure-parameters

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "style": {
        "avoid-types-on-closure-parameters": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/style/avoid-types-on-closure-parameters: <reason>