ZetCode

Dart Typedef Tutorial

last modified May 30, 2026

A typedef in Dart is a type alias — a named shorthand for a type that can be used wherever the full type would appear. Typedefs make long or complex type signatures readable and reusable. Since Dart 2.13, typedefs work for any type, not only function types.

Function Typedef

The most common use of a typedef is giving a name to a function signature.

typedef_fn.dart
typedef Compare = int Function(int a, int b);

int ascending(int a, int b) => a - b;
int descending(int a, int b) => b - a;

void sortWith(List<int> nums, Compare fn) {
  nums.sort(fn);
}

void main() {
  var nums = [5, 1, 4, 2, 3];

  sortWith(nums, ascending);
  print(nums); // [1, 2, 3, 4, 5]

  sortWith(nums, descending);
  print(nums); // [5, 4, 3, 2, 1]
}

Compare is a typedef for any function that takes two int parameters and returns an int. Both ascending and descending match the signature and can be passed wherever Compare is expected.

$ dart run typedef_fn.dart
[1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]

Generic Function Typedef

Typedefs can have type parameters, making them reusable across different types.

typedef_generic.dart
typedef Transformer<T, R> = R Function(T input);

R applyAll<T, R>(List<T> items, Transformer<T, R> fn) {
  return items.map(fn).last;
}

void main() {
  Transformer<int, String> intToStr = (n) => 'num:$n';
  Transformer<String, int> strToLen = (s) => s.length;

  var result1 = applyAll([1, 2, 3], intToStr);
  print(result1); // num:3

  var result2 = applyAll(['hi', 'hello', 'hey'], strToLen);
  print(result2); // 3
}

Transformer<T, R> describes any single-argument function that maps a value of type T to a value of type R. The type parameters are supplied when the typedef is used.

$ dart run typedef_generic.dart
num:3
3

Typedef for a Complex Generic Type

Typedefs are especially useful for aliasing complex nested generic types such as Map<String, List<int>>.

typedef_complex.dart
typedef ScoreMap = Map<String, List<int>>;

ScoreMap buildScores(List<String> names) {
  return {for (var n in names) n: []};
}

void addScore(ScoreMap scores, String name, int score) {
  scores[name]?.add(score);
}

double average(List<int> values) =>
    values.isEmpty ? 0 : values.reduce((a, b) => a + b) / values.length;

void main() {
  ScoreMap scores = buildScores(['Alice', 'Bob']);

  addScore(scores, 'Alice', 90);
  addScore(scores, 'Alice', 85);
  addScore(scores, 'Bob', 70);

  scores.forEach((name, vals) {
    print('$name: avg ${average(vals).toStringAsFixed(1)}');
  });
}

Without the typedef, every function signature would repeat Map<String, List<int>>. The alias ScoreMap makes the intent immediately clear.

$ dart run typedef_complex.dart
Alice: avg 87.5
Bob: avg 70.0

Typedef for Callbacks

A typedef that represents a function with no parameters and no return value is a common pattern for event callbacks. Flutter's VoidCallback is defined exactly this way.

typedef_callback.dart
typedef VoidCallback = void Function();

class Button {
  final String label;
  final VoidCallback onPressed;

  const Button({required this.label, required this.onPressed});

  void press() {
    print('[$label] pressed');
    onPressed();
  }
}

void main() {
  final btn = Button(
    label: 'Submit',
    onPressed: () => print('Form submitted!'),
  );

  btn.press();
}

VoidCallback is a self-documenting alias — any reader immediately understands the field holds a no-argument, no-return callback. Flutter defines this typedef in its foundation library and uses it throughout the framework.

$ dart run typedef_callback.dart
[Submit] pressed
Form submitted!

Typedef for a Predicate

Naming common predicate signatures keeps higher-order utility functions concise.

typedef_predicate.dart
typedef Predicate<T> = bool Function(T value);

List<T> keep<T>(List<T> items, Predicate<T> test) =>
    items.where(test).toList();

void main() {
  Predicate<int> isEven = (n) => n % 2 == 0;
  Predicate<String> isLong = (s) => s.length > 4;

  print(keep([1, 2, 3, 4, 5, 6], isEven));         // [2, 4, 6]
  print(keep(['hi', 'hello', 'hey', 'howdy'], isLong)); // [hello, howdy]
}

The same Predicate<T> alias works for any element type. Named predicates like isEven and isLong are also more readable at the call site than inline lambdas.

$ dart run typedef_predicate.dart
[2, 4, 6]
[hello, howdy]

Typedef for a Record Type

Dart 3 introduced records — anonymous immutable value types. A typedef gives a record type a meaningful name.

typedef_record.dart
typedef Point = ({double x, double y});
typedef Segment = (Point start, Point end);

double length(Segment seg) {
  final dx = seg.$2.x - seg.$1.x;
  final dy = seg.$2.y - seg.$1.y;
  return (dx * dx + dy * dy) == 0 ? 0 : _sqrt(dx * dx + dy * dy);
}

double _sqrt(double v) {
  double g = v / 2;
  for (var i = 0; i < 20; i++) g = (g + v / g) / 2;
  return g;
}

void main() {
  final Point a = (x: 0, y: 0);
  final Point b = (x: 3, y: 4);

  final Segment seg = (a, b);
  print('Length: ${length(seg).toStringAsFixed(2)}'); // 5.00

  print('Start: (${seg.$1.x}, ${seg.$1.y})');
  print('End:   (${seg.$2.x}, ${seg.$2.y})');
}

Without the typedefs, the function signatures would contain anonymous record types that obscure intent. Point and Segment turn structural types into domain concepts.

$ dart run typedef_record.dart
Length: 5.00
Start: (0.0, 0.0)
End:   (3.0, 4.0)

Summary

A Dart typedef creates a named alias for any type. The main use cases are:

Typedefs do not create new types — they are transparent aliases. A variable declared with a typedef is fully compatible with the underlying type and vice versa.

Source

Dart Language: Typedefs
Dart Language: Records

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.