avoid-dynamic

Same as avoid-dynamic in Dart Code Metrics

Path
lint/suspicious/avoid-dynamic
Default
Off, opt in
Origin
Dart Code Metrics
Rule
56 of 512

Flags explicit dynamic type annotations.

dynamic opts an expression out of static type checking, so member accesses and calls on it are resolved at runtime and any mistake surfaces as a crash rather than a compile error. Reaching for it is usually a shortcut where a concrete type, a generic, or Object? plus explicit checks would be safer. Prefer a precise type. dynamic used directly as a Map type argument — the documented JSON escape hatch, Map<String, dynamic> — is exempt, but a dynamic nested more deeply (such as Map<String, List<dynamic>>) is still flagged. All explicit type positions are checked, including record fields, type aliases, generic bounds, patterns, and types inside initializers.

Invalid

example.dartdart
// Test cases for avoid-dynamic rule
// All violations are annotated inline

void testDynamicVariable() {
  dynamic x = 5;
  dynamic name = "test";
  dynamic list = [1, 2, 3];
}

void testDynamicParameters(dynamic arg, dynamic other) {
  print(arg);
  print(other);
}

Future<dynamic> asyncMethod() {
  return Future.value(42);
}

dynamic returnDynamic() {
  return "anything";
}

class TestClass {
  dynamic field;

  void method(dynamic param) {
    final dynamic local = param;
  }
}

// `dynamic` as a type argument of a non-Map generic is still flagged, and a
// `dynamic` nested one level below `Map` (inside `List`) is NOT exempt: only a
// direct `Map<_, dynamic>` argument is.
List<dynamic> listOfDynamic = [];

Route<dynamic>? routeField;

Map<String, List<dynamic>> nested = {};

typedef DynamicAlias = List<dynamic>;

(dynamic, {dynamic value}) recordTypes;

class DynamicInitializers {
  final values = <dynamic>[];

  DynamicInitializers() {
    final local = <dynamic>[];
  }

  set value(Object value) {
    dynamic local = value;
  }

  Object operator +(Object other) {
    dynamic local = other;
    return local;
  }
}

void dynamicSwitch(Object input) {
  switch (input) {
    default:
      dynamic local = input;
  }
}

typedef BoundedFunction = void Function<T extends List<dynamic>>();

void dynamicPatterns(Object input) {
  if (input case List<dynamic> values) {
    print(values);
  }
  final values = [
    for (List<dynamic> value in <List<Object>>[]) value,
  ];
}

class DynamicBase extends Base<List<dynamic>> {}

@Annotation<List<dynamic>>()
void annotatedDynamic() {}

Object dynamicSwitchExpression(Object input) => switch (input) {
  List<dynamic> values => values,
  _ => input,
};
Avoid using the `dynamic` type.
4void testDynamicVariable() {
5 dynamic x = 5;
Avoid using the `dynamic` type.
5 dynamic x = 5;
6 dynamic name = "test";
Avoid using the `dynamic` type.
6 dynamic name = "test";
7 dynamic list = [1, 2, 3];
Avoid using the `dynamic` type.
9
10void testDynamicParameters(dynamic arg, dynamic other) {
Avoid using the `dynamic` type.
9
10void testDynamicParameters(dynamic arg, dynamic other) {
Avoid using the `dynamic` type.
14
15Future<dynamic> asyncMethod() {
Avoid using the `dynamic` type.
18
19dynamic returnDynamic() {
Avoid using the `dynamic` type.
23class TestClass {
24 dynamic field;
Avoid using the `dynamic` type.
25
26 void method(dynamic param) {
Avoid using the `dynamic` type.
26 void method(dynamic param) {
27 final dynamic local = param;
Avoid using the `dynamic` type.
33// direct `Map<_, dynamic>` argument is.
34List<dynamic> listOfDynamic = [];
Avoid using the `dynamic` type.
35
36Route<dynamic>? routeField;
Avoid using the `dynamic` type.
37
38Map<String, List<dynamic>> nested = {};
Avoid using the `dynamic` type.
39
40typedef DynamicAlias = List<dynamic>;
Avoid using the `dynamic` type.
41
42(dynamic, {dynamic value}) recordTypes;
Avoid using the `dynamic` type.
41
42(dynamic, {dynamic value}) recordTypes;
Avoid using the `dynamic` type.
44class DynamicInitializers {
45 final values = <dynamic>[];
Avoid using the `dynamic` type.
47 DynamicInitializers() {
48 final local = <dynamic>[];
Avoid using the `dynamic` type.
51 set value(Object value) {
52 dynamic local = value;
Avoid using the `dynamic` type.
55 Object operator +(Object other) {
56 dynamic local = other;
Avoid using the `dynamic` type.
63 default:
64 dynamic local = input;
Avoid using the `dynamic` type.
67
68typedef BoundedFunction = void Function<T extends List<dynamic>>();
Avoid using the `dynamic` type.
70void dynamicPatterns(Object input) {
71 if (input case List<dynamic> values) {
Avoid using the `dynamic` type.
74 final values = [
75 for (List<dynamic> value in <List<Object>>[]) value,
Avoid using the `dynamic` type.
78
79class DynamicBase extends Base<List<dynamic>> {}
Avoid using the `dynamic` type.
80
81@Annotation<List<dynamic>>()
Avoid using the `dynamic` type.
84Object dynamicSwitchExpression(Object input) => switch (input) {
85 List<dynamic> values => values,

Valid

example.dartdart
import 'dart:core' as core;

// Good cases for avoid-dynamic rule
// No violations expected

void testTypedVariable() {
  int x = 5;
  String name = "test";
  List<int> list = [1, 2, 3];
}

void testTypedParameters(int arg, String other) {
  print(arg);
  print(other);
}

Future<int> asyncMethod() {
  return Future.value(42);
}

String returnString() {
  return "anything";
}

class TestClass {
  Object field = Object();

  void method(Object param) {
    final Object local = param;
  }
}

Map<String, int> mapWithType = {};

List<String> listOfString = [];

void testWithGenerics<T>(T value) {
  print(value);
}

void testWithUnion(Object? nullable) {
  if (nullable != null) {
    print(nullable);
  }
}

// dcl exempts `dynamic` used directly as a `Map` type argument (the JSON escape
// hatch) — both in a `Map<...>` annotation and in a `<...>{}` map literal.
Map<String, dynamic> jsonField = {};

Map<String, dynamic> parseJson(Map<String, dynamic> json) {
  final literal = <String, dynamic>{'k': 1};
  return literal;
}

class Serializable {
  Map<String, dynamic> toJson() => <String, dynamic>{};
}

void mapConstructorArguments(Object value) {
  final empty = Map<String, dynamic>();
  final copied = Map<String, dynamic>.from({});
  if (value case <String, dynamic>{'key': var item}) {
    print(item);
  }
}

void prefixedMapConstructorArguments() {
  final empty = core.Map<String, dynamic>();
  final copied = core.Map<String, dynamic>.from({});
}

Configure avoid-dynamic

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

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

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

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