Watch the video (8 min)
On this page22 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. What Is a Typedef?
  5. 2. Naming a Function Type
  6. 3. Using the Alias Everywhere
  7. 4. Parameter Names in a Function Typedef
  8. 5. Naming a Long Collection Type
  9. 6. A Typedef Is Just an Alias
  10. 7. Typedef vs Extension Type
  11. 8. A Generic Typedef (Preview)
  12. 9. When a Typedef Helps — and When It Hides Too Much
  13. 10. A Real-World Example
  14. Code Examples
  15. Common Beginner Mistakes
  16. Best Practices
  17. Mini Challenge
  18. Practice Questions
  19. Practice Answers
  20. Quick Revision
  21. Key Takeaways
  22. What's Next?

Lecture 71 — Typedefs

Category11 — Enums, Extensions & Typedefs
Lecture071 of 140
Estimated video length8 min
TargetDart 3.12.2
LevelIntermediate

Types like double Function(double) and Map<String, List<int>> are correct but long, and they repeat everywhere. A typedef gives a type a short, meaningful name, and the type itself stays exactly the same.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.


Prerequisites

Students should already understand, from Lectures 035, 051 and 070:

  • function types such as double Function(double), and passing functions as arguments
  • maps and lists, including a Map<String, List<int>>
  • extension types, which create a separate type around an existing one

Connection With Previous Lectures

In Lecture 035, Sara's shop passed discount rules around as functions:

dart
void checkout(String customer, double total, double Function(double) discount) {
  print('$customer pays ${discount(total)}');
}

The type double Function(double) tells Dart the shape of the rule, but it does not tell a reader what the rule is. Write it in five more places and every line gets long and noisy. Lecture 070 built a brand-new type to fix a different problem. Here we want something lighter: the same type, just with a better name.


Learning Objectives

By the end of this lecture you will be able to:

  • Explain what a typedef is
  • Name a function type with typedef
  • Use a typedef for variables, parameters, and return types
  • Name a long collection type such as Map<String, List<int>>
  • Prove that a typedef is only an alias, not a new type
  • Contrast a typedef with an extension type
  • Read a generic typedef such as Check<int>
  • Decide when a typedef helps readability and when it hides too much

1. What Is a Typedef?

A typedef (short for type definition) gives an existing type a second name. That name is called a type alias.

Analogy — your phone saves a number as "Mum". Calling "Mum" dials exactly the same number; the contact name just makes it easier to recognize.

The syntax is typedef NewName = ExistingType;, written at the top level of the file.


2. Naming a Function Type

Give Sara's discount rule a name:

dart
typedef Discount = double Function(double);

double tenPercentOff(double total) => total * 0.9;

void checkout(String customer, double total, Discount discount) {
  print('$customer pays ${discount(total)}');
}

void main() {
  checkout('Sara', 1500, tenPercentOff);
}
output
Sara pays 1350.0

Discount means double Function(double). The parameter list now reads like a sentence: "a customer, a total, and a discount".


3. Using the Alias Everywhere

A typedef works anywhere a type is allowed — a variable, a parameter, or a return type:

dart
typedef Discount = double Function(double);

Discount ruleFor(bool isMember) {
  return isMember ? (total) => total * 0.8 : (total) => total;
}

void main() {
  Discount rule = ruleFor(true);
  print(rule(1000));
  rule = ruleFor(false);
  print(rule(1000));
}
output
800.0
1000.0

Compare the return type with Lecture 035's double Function(double) converterFor(...). The alias keeps the first line of the function short.


4. Parameter Names in a Function Typedef

You may name the parameters inside the function type. Dart ignores the names, but readers don't:

dart
typedef Grader = String Function(int marks);

String passFail(int marks) => marks >= 50 ? 'Pass' : 'Fail';

void main() {
  Grader grade = passFail;
  print(grade(72));
  print(grade(41));
}
output
Pass
Fail

String Function(int marks) says what the int is. A function with any parameter name still fits, as long as it takes one int and returns a String.


5. Naming a Long Collection Type

Lecture 051 stored each student's marks in a Map<String, List<int>>. A typedef names that shape once:

dart
typedef StudentMarks = Map<String, List<int>>;

void printTotals(StudentMarks marks) {
  for (var entry in marks.entries) {
    var total = entry.value.reduce((a, b) => a + b);
    print('${entry.key}: $total');
  }
}

void main() {
  StudentMarks marks = {
    'Ali': [80, 75, 85],
    'Sara': [90, 88, 92],
  };
  printTotals(marks);
}
output
Ali: 240
Sara: 270

StudentMarks is still a map: entries, [], forEach and everything else from Lectures 049–050 work unchanged.


6. A Typedef Is Just an Alias

A typedef does not create a new type. The alias and the original are the same thing, so values move between them freely:

dart
typedef StudentMarks = Map<String, List<int>>;

void main() {
  StudentMarks marks = {
    'Zayan': [60, 70],
  };
  Map<String, List<int>> plain = marks;
  StudentMarks back = plain;
  print(back);
}
output
{Zayan: [60, 70]}

No conversion is needed in either direction. When you run a program, Dart even reports errors using the original type. Pass the wrong function to checkout from section 2:

dart
typedef Discount = double Function(double);

int addOne(int n) => n + 1;

void checkout(String customer, double total, Discount discount) {
  print('$customer pays ${discount(total)}');
}

void main() {
  checkout('Ali', 1500, addOne);
}
output
Error: The argument type 'int Function(int)' can't be assigned to the parameter type 'double Function(double)'.
  checkout('Ali', 1500, addOne);
                        ^

The message never says Discount: the compiler only sees double Function(double). Your editor may print the alias name instead. Either way, it is the same type.


7. Typedef vs Extension Type

Lecture 070's extension types are the opposite: they create a separate type that Dart keeps apart from the original. Put the two side by side:

dart
typedef Marks = int;

extension type RollNumber(int value) {}

void main() {
  Marks marks = 80;
  int plain = marks; // ✓ Marks is int
  RollNumber roll = RollNumber(7);
  // int number = roll; // ✗
  print('$plain ${roll.value}');
}
output
80 7

Uncommenting the last assignment gives:

output
Error: A value of type 'RollNumber' can't be assigned to a variable of type 'int'.
  int number = roll;
               ^
typedefextension type
Creates a new type?No — a second nameYes
Mixing with the originalAlways allowedRejected
Use it forReadabilitySafety — keeping values apart

8. A Generic Typedef (Preview)

A typedef can have a type parameter in angle brackets, filled in when you use it:

dart
typedef Check<T> = bool Function(T);

void main() {
  Check<int> isAdult = (age) => age >= 18;
  Check<String> isLongName = (name) => name.length > 4;
  print(isAdult(20));
  print(isLongName('Ali'));
}
output
true
false

Check<int> means bool Function(int); Check<String> means bool Function(String). One alias covers every type. How <T> works is the subject of Lecture 072 — Generics Fundamentals.


9. When a Typedef Helps — and When It Hides Too Much

A good typedef replaces a long type with a name that adds meaning. A bad one hides a short type behind a name that adds nothing:

dart
typedef Text = String; // ✗ hides a simple type
typedef Discount = double Function(double); // ✓ long type, clear name

Text greet(Text name) => 'Hello, $name';
double tenPercentOff(double total) => total * 0.9;

void main() {
  Discount rule = tenPercentOff;
  print(greet('Sara'));
  print(rule(200));
}
output
Hello, Sara
180.0

Text makes readers stop and look it up, only to find String. A typedef earns its place when the type is long, repeats in several places, or needs a name that explains its job.

You may also meet an older form, typedef double Discount(double total);. It only works for function types; prefer the = form shown in this lecture.


10. A Real-World Example

dart
typedef StudentMarks = Map<String, List<int>>;
typedef Grader = String Function(int average);

String strict(int average) => average >= 80 ? 'A' : 'B';
String relaxed(int average) => average >= 60 ? 'A' : 'B';

void report(StudentMarks marks, Grader grade) {
  marks.forEach((name, list) {
    var average = list.reduce((a, b) => a + b) ~/ list.length;
    print('$name: $average (${grade(average)})');
  });
}

void main() {
  StudentMarks marks = {
    'Ali': [80, 75, 85],
    'Sara': [90, 88, 92],
    'Zayan': [60, 70, 65],
  };
  report(marks, strict);
  print('---');
  report(marks, relaxed);
}
output
Ali: 80 (A)
Sara: 90 (A)
Zayan: 65 (B)
---
Ali: 80 (A)
Sara: 90 (A)
Zayan: 65 (A)

report(StudentMarks marks, Grader grade) tells the reader everything at a glance. Without the aliases, its first line would be void report(Map<String, List<int>> marks, String Function(int) grade).


Code Examples

Example 1 — A Function Typedef

dart
typedef Greeting = String Function(String);

String hello(String name) => 'Hello, $name!';

void main() {
  Greeting greet = hello;
  print(greet('Ali'));
}
output
Hello, Ali!

Example 2 — A Typedef as a Parameter Type

dart
typedef Operation = int Function(int, int);

void show(int a, int b, Operation op) {
  print(op(a, b));
}

void main() {
  show(6, 3, (a, b) => a + b);
  show(6, 3, (a, b) => a * b);
}
output
9
18

Example 3 — A Typedef as a Return Type

dart
typedef Converter = double Function(double);

Converter converterFor(String unit) {
  return unit == 'F' ? (c) => c * 9 / 5 + 32 : (c) => c;
}

void main() {
  print(converterFor('F')(100));
  print(converterFor('C')(100));
}
output
212.0
100.0

Example 4 — A No-Parameter Function Type

dart
typedef Action = void Function();

void repeatTwice(Action action) {
  action();
  action();
}

void main() {
  repeatTwice(() => print('Hi, Zayan'));
}
output
Hi, Zayan
Hi, Zayan

Example 5 — A Collection Typedef

dart
typedef Prices = Map<String, int>;

void main() {
  Prices prices = {'Pen': 50, 'Notebook': 200};
  prices['Bag'] = 1500;
  print(prices);
}
output
{Pen: 50, Notebook: 200, Bag: 1500}

Example 6 — Alias and Original Are Interchangeable

dart
typedef Prices = Map<String, int>;

void main() {
  Map<String, int> shop = {'Pen': 50};
  Prices sameShop = shop;
  sameShop['Ink'] = 30;
  print(shop);
}
output
{Pen: 50, Ink: 30}

How it works

  1. sameShop is assigned straight from shop — no conversion, because both have the same type.
  2. Both names point to one map object, so adding 'Ink' through sameShop shows up in shop.

Example 7 — Student Marks

dart
typedef StudentMarks = Map<String, List<int>>;

int best(StudentMarks marks, String name) {
  var list = marks[name] ?? [];
  return list.isEmpty ? 0 : list.reduce((a, b) => a > b ? a : b);
}

void main() {
  StudentMarks marks = {
    'Ali': [80, 75, 85],
    'Sara': [90, 88, 92],
  };
  print('Ali best: ${best(marks, 'Ali')}');
  print('Sara best: ${best(marks, 'Sara')}');
  print('Zayan best: ${best(marks, 'Zayan')}');
}
output
Ali best: 85
Sara best: 92
Zayan best: 0

How it works

  1. marks[name] returns a nullable list; ?? [] gives an empty list for a missing student.
  2. reduce keeps the larger of each pair, leaving the highest mark.

Example 8 — Shopping Total with a Discount Rule

dart
typedef Discount = double Function(double);

double noDiscount(double total) => total;
double tenPercentOff(double total) => total * 0.9;

void checkout(String customer, List<double> items, Discount discount) {
  var total = items.reduce((a, b) => a + b);
  print('$customer pays ${discount(total)}');
}

void main() {
  checkout('Ali', [500, 1000], noDiscount);
  checkout('Sara', [500, 1000], tenPercentOff);
}
output
Ali pays 1500.0
Sara pays 1350.0

Example 9 — Bank Balance Transactions

dart
typedef Transaction = double Function(double balance);

void main() {
  List<Transaction> history = [(b) => b + 500, (b) => b - 200, (b) => b * 1.1];
  var balance = 1000.0;
  for (var step in history) {
    balance = step(balance);
  }
  print('Balance: ${balance.toStringAsFixed(2)}');
}
output
Balance: 1430.00

How it works

  1. List<Transaction> is far easier to read than List<double Function(double)>.
  2. The loop applies each transaction to the running balance: 1000 → 1500 → 1300 → 1430.

Example 10 — Temperature Rules with a Generic Typedef

dart
typedef Check<T> = bool Function(T);

void main() {
  Check<double> isHot = (t) => t > 35;
  var temperatures = [28.5, 37.0, 41.2];
  for (var t in temperatures) {
    print('$t: ${isHot(t) ? 'hot' : 'fine'}');
  }
}
output
28.5: fine
37.0: hot
41.2: hot

Common Beginner Mistakes

1. Writing a typedef inside a function

dart
void main() {
  // typedef Score = int; // ✗ typedefs belong at the top level
}
output
Error: Expected ';' after this.
  typedef Score = int;
  ^^^^^^^

Inside main(), Dart does not recognize typedef as a keyword at all. Move it above main().

2. Expecting a typedef to keep values apart

dart
typedef Marks = int;
typedef Age = int;

void main() {
  Marks marks = 80;
  Age age = marks; // no error: both are just int
  print(age);
}
output
80

No error appears, because Marks and Age are both int. If mixing them must be an error, use an extension type (Lecture 070).

3. Passing a function of the wrong shape

dart
typedef Discount = double Function(double);
int addOne(int n) => n + 1;

void main() {
  // Discount rule = addOne; // ✗
}
output
Error: A value of type 'int Function(int)' can't be assigned to a variable of type 'double Function(double)'.

The alias does not loosen the rules; the function must still match double Function(double).

4. Aliasing a type that is already short

typedef Text = String; makes readers look up a name that adds nothing. Alias long or repeated types only.

5. Choosing a name that doesn't describe the job

typedef Fn = double Function(double); is shorter but no clearer. Prefer Discount, Converter, or Transaction.


Best Practices

  • Use a typedef for long function types and nested collection types that repeat.
  • Name the alias after its job in UpperCamelCase: Discount, StudentMarks.
  • Name the parameters inside function typedefs when it helps: Function(int marks).
  • Use the typedef Name = Type; form, not the older function-only form.
  • Remember an alias is not a new type; use an extension type when values must stay apart.
  • Don't alias short, obvious types like int or String.

Mini Challenge

Create typedef PriceRule = double Function(double price);. Write void applyAll(double price, List<PriceRule> rules) that applies each rule in turn and prints the final price. Use a rule that adds 100 delivery and a rule that takes 20% off, starting from 1000.

Your program should print exactly:

output
Final price: 880.0

Try it yourself before opening the solution.

Show solution
dart
typedef PriceRule = double Function(double price);

void applyAll(double price, List<PriceRule> rules) {
  for (var rule in rules) {
    price = rule(price);
  }
  print('Final price: $price');
}

void main() {
  applyAll(1000, [(p) => p + 100, (p) => p * 0.8]);
}
output
Final price: 880.0

How it works

  1. List<PriceRule> is a list of functions, each double Function(double).
  2. 1000 + 100 gives 1100.0; 1100.0 * 0.8 gives 880.0.

Practice Questions

Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.

Beginner

  1. Write typedef Shout = String Function(String);, store a function that returns its text in upper case, and print the result for 'sara'.
  2. Write typedef Cart = List<int>;, create a Cart with 200, 350, 50, and print it.
  3. Is typedef Score = int; a new type? Write a program that assigns a Score of 95 to an int variable and prints it.

Application

  1. Write typedef MathOp = int Function(int, int); and a function calculate(int a, int b, MathOp op). Print the results for 10 and 4 with subtraction and multiplication.
  2. Write typedef Inventory = Map<String, int>; and a function int totalItems(Inventory inv). Print the total for {'Pen': 10, 'Ink': 5, 'Pad': 3}.
  3. Write typedef Greeter = String Function(String name); and a function Greeter greeterFor(int hour) returning a "Good morning" greeter before 12 and a "Good evening" greeter otherwise. Print the greetings for Ali at 9 and at 20.
  4. Rewrite this first line using two typedefs, ClassList and Formatter: void printClass(Map<String, List<String>> classes, String Function(String) format). Then call it with {'7A': ['Ali', 'Sara']} and a formatter that adds '- ' in front.

Challenge

  1. Write typedef Check<T> = bool Function(T); and int countWhere(List<int> values, Check<int> test). Print how many of [45, 82, 67, 91, 38] are passing marks (>= 50).
  2. Write typedef Transaction = double Function(double);. Apply a deposit of 250 and a withdrawal of 100 to Zayan's balance of 500.0 using a List<Transaction>, and print Balance: 650.0.
  3. Write typedef Celsius = double; and typedef Fahrenheit = double;, then assign a Fahrenheit value to a Celsius variable. Does Dart stop you? Explain in one sentence which feature you would use if it should.

Practice Answers

Question 1 — Solution

dart
typedef Shout = String Function(String);

void main() {
  Shout shout = (text) => text.toUpperCase();
  print(shout('sara'));
}
output
SARA

Question 2 — Solution

dart
typedef Cart = List<int>;

void main() {
  Cart cart = [200, 350, 50];
  print(cart);
}
output
[200, 350, 50]

Question 3 — Solution

No. Score is only another name for int, so the assignment needs no conversion.

dart
typedef Score = int;

void main() {
  Score score = 95;
  int plain = score;
  print(plain);
}
output
95

Question 4 — Solution

dart
typedef MathOp = int Function(int, int);

int calculate(int a, int b, MathOp op) => op(a, b);

void main() {
  print(calculate(10, 4, (a, b) => a - b));
  print(calculate(10, 4, (a, b) => a * b));
}
output
6
40

Question 5 — Solution

dart
typedef Inventory = Map<String, int>;

int totalItems(Inventory inv) => inv.values.reduce((a, b) => a + b);

void main() {
  print(totalItems({'Pen': 10, 'Ink': 5, 'Pad': 3}));
}
output
18

Question 6 — Solution

dart
typedef Greeter = String Function(String name);

Greeter greeterFor(int hour) {
  return hour < 12
      ? (name) => 'Good morning, $name'
      : (name) => 'Good evening, $name';
}

void main() {
  print(greeterFor(9)('Ali'));
  print(greeterFor(20)('Ali'));
}
output
Good morning, Ali
Good evening, Ali

Question 7 — Solution

dart
typedef ClassList = Map<String, List<String>>;
typedef Formatter = String Function(String);

void printClass(ClassList classes, Formatter format) {
  classes.forEach((room, students) {
    print(room);
    for (var s in students) {
      print(format(s));
    }
  });
}

void main() {
  printClass({
    '7A': ['Ali', 'Sara'],
  }, (s) => '- $s');
}
output
7A
- Ali
- Sara

Question 8 — Solution

dart
typedef Check<T> = bool Function(T);

int countWhere(List<int> values, Check<int> test) {
  var count = 0;
  for (var v in values) {
    if (test(v)) count++;
  }
  return count;
}

void main() {
  print(countWhere([45, 82, 67, 91, 38], (m) => m >= 50));
}
output
3

Question 9 — Solution

dart
typedef Transaction = double Function(double);

void main() {
  List<Transaction> steps = [(b) => b + 250, (b) => b - 100];
  var balance = 500.0;
  for (var step in steps) {
    balance = step(balance);
  }
  print('Balance: $balance');
}
output
Balance: 650.0

Question 10 — Solution

dart
typedef Celsius = double;
typedef Fahrenheit = double;

void main() {
  Fahrenheit hot = 104.0;
  Celsius mixedUp = hot;
  print(mixedUp);
}
output
104.0

Dart does not stop you: both aliases are just double. To make mixing them an error, use extension types (Lecture 070), which create separate types.


Quick Revision

  1. What does typedef do?
  2. Where in a file must a typedef be written?
  3. Write a typedef for a function that takes a double and returns a double.
  4. Can you assign a StudentMarks value to a Map<String, List<int>> variable?
  5. Which type name does dart run show in error messages — the alias or the original?
  6. How does a typedef differ from an extension type?
  7. What does Check<int> mean if typedef Check<T> = bool Function(T);?
  8. Why is typedef Text = String; a poor typedef?

Key Takeaways

  • typedef Name = Type; gives an existing type a second name, a type alias.
  • Typedefs shine for long function types (Discount) and nested collections (StudentMarks).
  • An alias works anywhere a type does: variables, parameters, return types, lists.
  • A typedef is not a new type: alias and original mix freely.
  • Extension types create a separate type; typedefs only rename.
  • Typedefs can take type parameters, like Check<T>; generics come in Lecture 072.
  • Alias types that are long or repeated, and name them after their job.

What's Next?

Lecture 72 — Generics Fundamentals. This closes Category 11. Along the way we kept writing angle brackets — List<int>, Map<String, List<int>>, and today's Check<T> — without asking how they work. Next we will learn what a type parameter is, why List<int> and List<String> are different types, and how generics let one piece of code work safely with any type.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.