avoid-dynamic-calls

Same as avoid_dynamic_calls in Dart lints

Path
lint/correctness/avoid-dynamic-calls
Default
On (recommended)
Origin
Dart lints
Rule
57 of 512
recommended

Avoid method calls or property accesses on a dynamic target.

Members reached through dynamic are looked up at runtime: typos and signature mismatches surface as NoSuchMethodError instead of compile errors, and the dispatch is slower:

void log(dynamic event) {
  print(event.name); // BAD
}

void log(Event event) {
  print(event.name); // GOOD
}

Reported targets: member accesses, method calls, index operations, user-definable binary operators (not ==/!=), prefix and postfix operators, and compound assignments on a dynamic receiver, plus calls of a dynamic or Function-typed variable, including .call() cascade sections. hashCode, runtimeType, toString, and noSuchMethod — members every object has — are allowed (except method calls directly on cascade targets), as are explicit as dynamic casts and x!.

Receiver and callable types come from the semantic resolver, including flow-promoted types at each use site. Diagnostics retain grouped receiver spans; compound updates use the complete update expression, and selector chains retain their cascade-section start.

Invalid

example.dartdart
dynamic config;

void member(dynamic event) {
  print(event.name);
  event.handle();
  event?.handle();
}

void operators(dynamic a) {
  print(a + 1);
  print(a < 2);
  print(-a);
  a++;
  a += 1;
  print(a[0]);
}

void invocation(dynamic callback, Function handler) {
  callback();
  handler(1);
}

void json(Map<String, dynamic> data, List<dynamic> items) {
  print(data['user'].name);
  print(data['user']['id']);
  items.first;
  print(items[0].length);
}

void chained(dynamic value) {
  dynamic local = value;
  local.first.last;
  local..add(1)..add(2);
}

void topLevel() {
  config.load();
}

class Holder {
  dynamic payload;

  void read() {
    print(payload.length);
  }
}

void promotedInOneFunction(dynamic scopedValue) {
  if (scopedValue is String) {
    print(scopedValue.length);
  }
}

void stillDynamicInAnotherFunction(dynamic scopedValue) {
  print(scopedValue.length);
}

void cascadeFunction(Function callback) {
  callback
    ..call()
    ..call();
}
Avoid invoking methods or accessing properties on a `dynamic` target.
3void member(dynamic event) {
4 print(event.name);
Avoid invoking methods or accessing properties on a `dynamic` target.
4 print(event.name);
5 event.handle();
Avoid invoking methods or accessing properties on a `dynamic` target.
5 event.handle();
6 event?.handle();
Avoid invoking methods or accessing properties on a `dynamic` target.
9void operators(dynamic a) {
10 print(a + 1);
Avoid invoking methods or accessing properties on a `dynamic` target.
10 print(a + 1);
11 print(a < 2);
Avoid invoking methods or accessing properties on a `dynamic` target.
11 print(a < 2);
12 print(-a);
Avoid invoking methods or accessing properties on a `dynamic` target.
12 print(-a);
13 a++;
Avoid invoking methods or accessing properties on a `dynamic` target.
13 a++;
14 a += 1;
Avoid invoking methods or accessing properties on a `dynamic` target.
14 a += 1;
15 print(a[0]);
Avoid invoking methods or accessing properties on a `dynamic` target.
18void invocation(dynamic callback, Function handler) {
19 callback();
Avoid invoking methods or accessing properties on a `dynamic` target.
19 callback();
20 handler(1);
Avoid invoking methods or accessing properties on a `dynamic` target.
23void json(Map<String, dynamic> data, List<dynamic> items) {
24 print(data['user'].name);
Avoid invoking methods or accessing properties on a `dynamic` target.
24 print(data['user'].name);
25 print(data['user']['id']);
Avoid invoking methods or accessing properties on a `dynamic` target.
26 items.first;
27 print(items[0].length);
Avoid invoking methods or accessing properties on a `dynamic` target.
31 dynamic local = value;
32 local.first.last;
Avoid invoking methods or accessing properties on a `dynamic` target.
31 dynamic local = value;
32 local.first.last;
Avoid invoking methods or accessing properties on a `dynamic` target.
32 local.first.last;
33 local..add(1)..add(2);
Avoid invoking methods or accessing properties on a `dynamic` target.
36void topLevel() {
37 config.load();
Avoid invoking methods or accessing properties on a `dynamic` target.
43 void read() {
44 print(payload.length);
Avoid invoking methods or accessing properties on a `dynamic` target.
54void stillDynamicInAnotherFunction(dynamic scopedValue) {
55 print(scopedValue.length);
Avoid invoking methods or accessing properties on a `dynamic` target.
59 callback
60 ..call()
Avoid invoking methods or accessing properties on a `dynamic` target.
60 ..call()
61 ..call();

Valid

example.dartdart
import 'dart:async';

dynamic config;

void allowed(dynamic value) {
  print(value.hashCode);
  print(value.runtimeType);
  print(value.toString());
  print(value == 1);
  print(value != null);
  value!;
  print(value ?? 1);
}

void casts(dynamic value) {
  (value as dynamic).foo();
  (value as String).length;
  value = 2;
}

void typed(String text, List<int> numbers, Map<String, int> counts) {
  print(text.length);
  print(numbers.first.isEven);
  print(counts['a']);
}

void untypedClosure(List<String> names) {
  names.forEach((name) => print(name.length));
  final upper = names.map((name) => name.toUpperCase());
  print(upper);
}

void untypedLiteral() {
  final values = [1, 2];
  print(values.first.isEven);
}

class Holder {
  String config = '';

  void read() {
    print(config.length);
  }
}

void functionType(void Function() callback, int Function(int) mapper) {
  callback();
  print(mapper(1));
}

void shadowed(String value) {
  print(value.length);
}

void otherShadowed(dynamic value) {
  print(value.hashCode);
}

void promotedMapAndString(dynamic data) {
  if (data is Map) {
    print(data['key']);
  }
  if (data is String) {
    print(data.length);
  }
}

void promotedByEarlyReturn(dynamic list) {
  if (list is! List) return;
  print(list.length);
}

void collectionForShadow(dynamic item, List<String> items) {
  print([for (final String item in items) item.length]);
}

void acceptsTypedCallback(void Function(String) callback) {}

void contextualParameters() {
  acceptsTypedCallback((value) => print(value.length));
  void Function(String) callback = (value) => print(value.length);
  callback('text');
}

void unresolvedExternalContext(Stream<String> events) {
  events.listen((event) => print(event.length));
}

void acceptsNamedCallback(void Function({required String value}) callback) {}

void namedContextualParameters() {
  acceptsNamedCallback(({required value}) => print(value.length));
}

void acceptsGenericCallback(void Function<T>(T value) callback) {}

void genericContextualParameters() {
  acceptsGenericCallback(<T>(value) {
    T typed = value;
    print(typed);
  });
}

void cascadeCallback(List<int> values) {
  values..sort((left, right) => left.compareTo(right));
}

void nullCoalescingCallback() {
  void Function(String)? callback;
  callback ??= (value) => print(value.length);
  callback('text');
}

void explicitlyTypedClosureCollections() {
  final callbacks = <void Function(String)>[
    (value) => print(value.length),
  ];
  final callbackSet = <void Function(String)>{
    (value) => print(value.length),
  };
  final callbackMap = <String, void Function(Map<String, int>)>{
    'count': (value) => print(value.length),
  };
  print([callbacks, callbackSet, callbackMap]);
}

void contextuallyTypedClosureCollections() {
  List<void Function(String)> callbacks = [
    (value) => print(value.length),
  ];
  Map<String, void Function(String)> callbackMap = {
    'text': (value) => print(value.length),
  };
  print([callbacks, callbackMap]);
}

void explicitlyCastFunctionCascade(Object callback) {
  (callback as Function)..call();
}

Configure avoid-dynamic-calls

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

falcon.jsonjson
{
  "linter": {
    "rules": {
      "correctness": {
        "avoid-dynamic-calls": "error"
      }
    }
  }
}

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

example.dartdart
// falcon-ignore lint/correctness/avoid-dynamic-calls: <reason>