Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Dart type annotation


May 23, 2021 Dart Code style guide


Table of contents


Type annotation

For public APIs, it is a good idea to provide type annotations.

Type annotations are a very important document that explains how the appropriate library should be used. Return type annotations for parameters and public methods help consumers understand what parameters the API needs and what functionality it provides.

However, if you have an API that can receive any parameters, or a value that cannot be represented in Dart, the APi can not add annotations.

For code inside the library, even private, or nested functions, add annotations where you think they are helpful, but don't think you have to provide them.

install(id, destPath) {    // bad
  // ...
}

In the code above, we don't know id is. S tring? S o destPath S tring or file object? Is this function synchronized or asynchronous?

Future<bool> install(PackageId id, String destPath) {     // good
  // ...
}

When you add a type, the information about this function becomes clear.

For local variables, it is best to declare var instead of type annotations.

Now, we prefer to keep the code of the function body as concise as possible, and the type of the local variable can be inferred in the initialization expression, in which case it is not necessary to display a declaration of its type. A good editor can infer the type of local variable, so auto-complement is still available, and other features you expect will work.

Map<int, List<Person>> groupByZip(Iterable<Person> people) {    // good 
  var peopleByZip = new Map<int, List<Person>>();
  for (var person in people) {
    peopleByZip.putIfAbsent(person.zip, () => <Person>[]);
    peopleByZip[person.zip].add(person);
  }
  return peopleByZip;
}
Map<int, List<Person>> groupByZip(Iterable<Person> people) {    // bad
  Map<int, List<Person>> peopleByZip = new Map<int, List<Person>>();
  for (Person person in people) {
    peopleByZip.putIfAbsent(person.zip, () => <Person>[]);
    peopleByZip[person.zip].add(person);
  }
  return peopleByZip;
}

If there are requirements for the running performance of your code, it is best to replace num with double or double int passing num

A single call point (with a stable input type) is easier to optimize than a polymorphic call point, whose input type may change.

For number types, you should indicate the specific number type when you add type annotations. E xplicitly declaring double or int people who use your method pass the appropriate parameters.

Do not make type annotations when formally initializing.

If a constructor's argument uses this. to initialize a this. the argument must be of the same type as this field, so you don't have to use type annotations.

class Point {    // good
  int x, y;
  Point(this.x, this.y);
}
class Point {    // bad
  int x, y;
  Point(int this.x, int this.y);
}

You should avoid using type annotations in function expressions.

One of the great advantages of a function expression is its simplicity. I f a function is so complex that it needs an annotation type to understand it, it should be a function statement or a method. Accordingly, if a function is simple enough to be used as an expression, it does not require type annotations.

var names = people.map((person) => person.name);    // good
var names = people.map((Person person) {    // bad
  return person.name;
});

You should avoid using dynamic to add type annotations when you don't need them.

In Dart, type annotations are negligible in most cases because they are automatically treated as dynamic Therefore, not adding type annotations is semantically completely no problem, and the code appears more concise.

lookUpOrDefault(String name, Map map, defaultValue) {    // good
  var value = map[name];
  if (value != null) return value;
  return defaultValue;
}
dynamic lookUpOrDefault(String name, Map map, dynamic defaultValue) {    // bad
  var value = map[name];
  if (value != null) return value;
  return defaultValue;
}

For any object received, object should be used Object of dynamic indicate that the argument is an object.

Some operations may apply to any object. F or example, toString() used to output information and can be used for any object. T here are two types in Dart that can match all objects: Object and dynamic However, they represent two different things.

Object annotations indicate that "I can receive any object as long as it contains a method defined by the related object itself."

dynamic annotations indicate that there are no annotations to indicate what type of object you actually allow. (There may be, but you don't care if you want to declare)

//good
// Accepts any object.
void log(Object object) {
  print(object.toString());
}

// Only accepts bool or String, which can't be expressed in a type annotation.
bool convertToBool(arg) {
  if (arg is bool) return arg;
  if (arg is String) return arg == 'true';
  throw new ArgumentError('Cannot convert $arg to a bool.');
}