On this page22 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is a Typedef?
- 2. Naming a Function Type
- 3. Using the Alias Everywhere
- 4. Parameter Names in a Function Typedef
- 5. Naming a Long Collection Type
- 6. A Typedef Is Just an Alias
- 7. Typedef vs Extension Type
- 8. A Generic Typedef (Preview)
- 9. When a Typedef Helps — and When It Hides Too Much
- 10. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 71 — Typedefs
| Category | 11 — Enums, Extensions & Typedefs |
| Lecture | 071 of 140 |
| Estimated video length | 8 min |
| Target | Dart 3.12.2 |
| Level | Intermediate |
Types like
double Function(double)andMap<String, List<int>>are correct but long, and they repeat everywhere. Atypedefgives 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:
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
typedefis - 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:
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);
}Sara pays 1350.0Discount 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:
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));
}800.0
1000.0Compare 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:
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));
}Pass
FailString 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:
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);
}Ali: 240
Sara: 270StudentMarks 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:
typedef StudentMarks = Map<String, List<int>>;
void main() {
StudentMarks marks = {
'Zayan': [60, 70],
};
Map<String, List<int>> plain = marks;
StudentMarks back = plain;
print(back);
}{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:
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);
}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:
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}');
}80 7Uncommenting the last assignment gives:
Error: A value of type 'RollNumber' can't be assigned to a variable of type 'int'.
int number = roll;
^typedef | extension type | |
|---|---|---|
| Creates a new type? | No — a second name | Yes |
| Mixing with the original | Always allowed | Rejected |
| Use it for | Readability | Safety — keeping values apart |
8. A Generic Typedef (Preview)
A typedef can have a type parameter in angle brackets, filled in when you use it:
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'));
}true
falseCheck<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:
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));
}Hello, Sara
180.0Text 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
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);
}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
typedef Greeting = String Function(String);
String hello(String name) => 'Hello, $name!';
void main() {
Greeting greet = hello;
print(greet('Ali'));
}Hello, Ali!Example 2 — A Typedef as a Parameter Type
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);
}9
18Example 3 — A Typedef as a Return Type
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));
}212.0
100.0Example 4 — A No-Parameter Function Type
typedef Action = void Function();
void repeatTwice(Action action) {
action();
action();
}
void main() {
repeatTwice(() => print('Hi, Zayan'));
}Hi, Zayan
Hi, ZayanExample 5 — A Collection Typedef
typedef Prices = Map<String, int>;
void main() {
Prices prices = {'Pen': 50, 'Notebook': 200};
prices['Bag'] = 1500;
print(prices);
}{Pen: 50, Notebook: 200, Bag: 1500}Example 6 — Alias and Original Are Interchangeable
typedef Prices = Map<String, int>;
void main() {
Map<String, int> shop = {'Pen': 50};
Prices sameShop = shop;
sameShop['Ink'] = 30;
print(shop);
}{Pen: 50, Ink: 30}How it works
sameShopis assigned straight fromshop— no conversion, because both have the same type.- Both names point to one map object, so adding
'Ink'throughsameShopshows up inshop.
Example 7 — Student Marks
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')}');
}Ali best: 85
Sara best: 92
Zayan best: 0How it works
marks[name]returns a nullable list;?? []gives an empty list for a missing student.reducekeeps the larger of each pair, leaving the highest mark.
Example 8 — Shopping Total with a Discount Rule
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);
}Ali pays 1500.0
Sara pays 1350.0Example 9 — Bank Balance Transactions
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)}');
}Balance: 1430.00How it works
List<Transaction>is far easier to read thanList<double Function(double)>.- The loop applies each transaction to the running balance:
1000 → 1500 → 1300 → 1430.
Example 10 — Temperature Rules with a Generic Typedef
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'}');
}
}28.5: fine
37.0: hot
41.2: hotCommon Beginner Mistakes
1. Writing a typedef inside a function
void main() {
// typedef Score = int; // ✗ typedefs belong at the top level
}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
typedef Marks = int;
typedef Age = int;
void main() {
Marks marks = 80;
Age age = marks; // no error: both are just int
print(age);
}80No 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
typedef Discount = double Function(double);
int addOne(int n) => n + 1;
void main() {
// Discount rule = addOne; // ✗
}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
intorString.
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:
Final price: 880.0Try it yourself before opening the solution.
Show solution
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]);
}Final price: 880.0How it works
List<PriceRule>is a list of functions, eachdouble Function(double).1000 + 100gives1100.0;1100.0 * 0.8gives880.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
- Write
typedef Shout = String Function(String);, store a function that returns its text in upper case, and print the result for'sara'. - Write
typedef Cart = List<int>;, create aCartwith200,350,50, and print it. - Is
typedef Score = int;a new type? Write a program that assigns aScoreof95to anintvariable and prints it.
Application
- Write
typedef MathOp = int Function(int, int);and a functioncalculate(int a, int b, MathOp op). Print the results for10and4with subtraction and multiplication. - Write
typedef Inventory = Map<String, int>;and a functionint totalItems(Inventory inv). Print the total for{'Pen': 10, 'Ink': 5, 'Pad': 3}. - Write
typedef Greeter = String Function(String name);and a functionGreeter greeterFor(int hour)returning a "Good morning" greeter before12and a "Good evening" greeter otherwise. Print the greetings for Ali at9and at20. - Rewrite this first line using two typedefs,
ClassListandFormatter: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
- Write
typedef Check<T> = bool Function(T);andint countWhere(List<int> values, Check<int> test). Print how many of[45, 82, 67, 91, 38]are passing marks (>= 50). - Write
typedef Transaction = double Function(double);. Apply a deposit of250and a withdrawal of100to Zayan's balance of500.0using aList<Transaction>, and printBalance: 650.0. - Write
typedef Celsius = double;andtypedef Fahrenheit = double;, then assign aFahrenheitvalue to aCelsiusvariable. Does Dart stop you? Explain in one sentence which feature you would use if it should.
Practice Answers
Question 1 — Solution
typedef Shout = String Function(String);
void main() {
Shout shout = (text) => text.toUpperCase();
print(shout('sara'));
}SARAQuestion 2 — Solution
typedef Cart = List<int>;
void main() {
Cart cart = [200, 350, 50];
print(cart);
}[200, 350, 50]Question 3 — Solution
No. Score is only another name for int, so the assignment needs no conversion.
typedef Score = int;
void main() {
Score score = 95;
int plain = score;
print(plain);
}95Question 4 — Solution
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));
}6
40Question 5 — Solution
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}));
}18Question 6 — Solution
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'));
}Good morning, Ali
Good evening, AliQuestion 7 — Solution
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');
}7A
- Ali
- SaraQuestion 8 — Solution
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));
}3Question 9 — Solution
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');
}Balance: 650.0Question 10 — Solution
typedef Celsius = double;
typedef Fahrenheit = double;
void main() {
Fahrenheit hot = 104.0;
Celsius mixedUp = hot;
print(mixedUp);
}104.0Dart 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
- What does
typedefdo? - Where in a file must a typedef be written?
- Write a typedef for a function that takes a
doubleand returns adouble. - Can you assign a
StudentMarksvalue to aMap<String, List<int>>variable? - Which type name does
dart runshow in error messages — the alias or the original? - How does a typedef differ from an extension type?
- What does
Check<int>mean iftypedef Check<T> = bool Function(T);? - 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.