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 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 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 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 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<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 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:
- Naming function signatures to improve readability at call sites
- Adding type parameters to make an alias reusable across types
- Shortening long nested generics like
Map<String, List<int>> - Documenting callback contracts (e.g.,
VoidCallback) - Giving record types meaningful domain names in Dart 3
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
List all Dart tutorials.