YouTip LogoYouTip

Dart Typedef

typedef is a type alias mechanism in Dart that allows you to define short, readable names for complex types.

\\\\n\\\\n

This chapter introduces two uses of typedef: traditional function type aliases, and generic type aliases introduced in Dart 3.0.

\\\\n\\\\n
\\\\n\\\\n

typedef Function Type Alias

\\\\n\\\\n

When a function type is used frequently, typedef can give it a clear name.

\\\\n\\\\n

This is particularly useful for callback functions, event handlers, and similar scenarios.

\\\\n\\\\n

Example

\\\\n\\\\n
// Define a function type alias\\\\n// Represents: a function that takes two int parameters and returns an int\\\\n\\\\ntypedef IntOperation = int Function(int a, int b);\\\\n\\\\n// Define a callback type\\\\n// Represents: a function that takes a String message and returns void\\\\n\\\\ntypedef MessageCallback = void Function(String message);\\\\n\\\\n// Use typedef as parameter type\\\\n\\\\nint performOperation(int x, int y, IntOperation operation) {\\\\n  return operation(x, y);\\\\n}\\\\n\\\\nvoid logMessage(String msg, MessageCallback callback) {\\\\n  print('Preparing to output message...');\\\\n  callback(' $msg');\\\\n}\\\\n\\\\nvoid main() {\\\\n  // Pass a function that matches the IntOperation signature\\\\n  int add(int a, int b) => a + b;\\\\n  int multiply(int a, int b) => a * b;\\\\n\\\\n  print('10 + 5 = ${performOperation(10, 5, add)}');\\\\n  print('10 Γ— 5 = ${performOperation(10, 5, multiply)}');\\\\n\\\\n  // Can also pass an anonymous function directly\\\\n  int result = performOperation(20, 4, (a, b) => a ~/ b);\\\\n  print('20 Γ· 4 = $result');\\\\n\\\\n  // Use MessageCallback\\\\n  logMessage('Operation completed', (msg) {\\\\n    print('Message received: $msg');\\\\n  });\\\\n}\\\\n
\\\\n\\\\n
10 + 5 = 15\\\\n10 Γ— 5 = 50\\\\n20 Γ· 4 = 5\\\\nPreparing to output message...\\\\nMessage received:  Operation completed
\\\\n\\\\n

Without typedef, you would need to repeatedly write verbose function type signatures at every parameter position.

\\\\n\\\\n

With typedef, type declarations become clear and unified, and modifications only need to be made in one place.

\\\\n\\\\n
\\\\n

typedef is just an alias for a type; it does not create a new type. IntOperation and int Function(int, int) are completely equivalent in the type system.

\\\\n
\\\\n\\\\n
\\\\n\\\\n

Function Types as Parameters

\\\\n\\\\n

In Dart, functions are first-class citizens and can be passed like ordinary values.

\\\\n\\\\n

Example

\\\\n\\\\n
// Directly use function type to declare parameters (without typedef)\\\\nvoid processNumbers(\\\\n  List numbers,\\\\n  bool Function(int) filter,\\\\n  String Function(int) formatter,\\\\n) {\\\\n  var filtered = numbers.where(filter);\\\\n  for (var n in filtered) {\\\\n    print(formatter(n));\\\\n  }\\\\n}\\\\n\\\\n// Higher-order function that returns a function\\\\nint Function(int) makeMultiplier(int factor) {\\\\n  // The returned closure captures factor\\\\n  return (int n) => n * factor;\\\\n}\\\\n\\\\nvoid main() {\\\\n  var scores = [55, 78, 92, 60, 45, 88];\\\\n\\\\n  print('--- Passing score ---');\\\\n  processNumbers(\\\\n    scores,\\\\n    (n) => n >= 60,          // filter condition\\\\n    (n) => 'TUTORIAL Minutenumber: $n Minute', // formatter\\\\n  );\\\\n\\\\n  print('--- High Minute(> 80οΌ‰---');\\\\n  processNumbers(\\\\n    scores,\\\\n    (n) => n > 80,\\\\n    (n) => 'High Minute: $n Minute',\\\\n  );\\\\n\\\\n  // Function as return value\\\\n  var doubler = makeMultiplier(2);\\\\n  var tripler = makeMultiplier(3);\\\\n\\\\n  print('5 Γ— 2 = ${doubler(5)}');\\\\n  print('5 Γ— 3 = ${tripler(5)}');\\\\n}\\\\n
\\\\n\\\\n
--- Passing score ---\\\\nTUTORIAL Minutenumber: 78 Minute\\\\nTUTORIAL Minutenumber: 92 Minute\\\\nTUTORIAL Minutenumber: 60 Minute\\\\nTUTORIAL Minutenumber: 88 Minute\\\\n--- High Minute(> 80οΌ‰---\\\\nHigh Minute: 92 Minute\\\\nHigh Minute: 88 Minute\\\\n5 Γ— 2 = 10\\\\n5 Γ— 3 = 15
\\\\n\\\\n
\\\\n\\\\n

Callback Pattern

\\\\n\\\\n

The callback pattern is the most common application scenario for function types.

\\\\n\\\\n

It gives control of "what to do" to the caller, making code more flexible and reusable.

\\\\n\\\\n

Example

\\\\n\\\\n
// Define callback types\\\\ntypedef ResultCallback = void Function(T result);\\\\ntypedef ErrorCallback = void Function(String error);\\\\n\\\\n// Simulate asynchronous operation\\\\nvoid fetchUserData(\\\\n  String userId, {\\\\n  required ResultCallback> onSuccess,\\\\n  required ErrorCallback onError,\\\\n}) {\\\\n  // Simulate network request\\\\n  print('Fetching User data...');\\\\n\\\\n  // Simulate success/failure\\\\n  if (userId == 'tutorial') {\\\\n    var data = {\\\\n      'id': 'tutorial',\\\\n      'name': 'TUTORIAL User',\\\\n      'level': 'VIP',\\\\n    };\\\\n    onSuccess(data);\\\\n  } else {\\\\n    onError('User $userId Does not exist');\\\\n  }\\\\n}\\\\n\\\\nvoid main() {\\\\n  // Use callbacks to handle results\\\\n  fetchUserData(\\\\n    'tutorial',\\\\n    onSuccess: (data) {\\\\n      print('Fetch Success!');\\\\n      print('Username: ${data['name']}');\\\\n      print('Level: ${data['level']}');\\\\n    },\\\\n    onError: (error) {\\\\n      print('Fetch Failure: $error');\\\\n    },\\\\n  );\\\\n\\\\n  print('---');\\\\n\\\\n  // Test failure case\\\\n  fetchUserData(\\\\n    'unknown',\\\\n    onSuccess: (data) {\\\\n      print('Fetch Success');\\\\n    },\\\\n    onError: (error) {\\\\n      print('Fetch Failure: $error');\\\\n    },\\\\n  );\\\\n}\\\\n
\\\\n\\\\n
Fetching User data...\\\\nFetch Success!\\\\nUsername: TUTORIAL User\\\\nLevel: VIP\\\\n---\\\\nFetching User data...\\\\nFetch Failure: User unknown Does not exist
\\\\n\\\\n

Common application scenarios for the callback pattern:

\\\\n\\\\n\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n
ScenarioCallback Type Example
Network requestonSuccess(data) / onError(error)
UI eventonTap() / onLongPress()
Data transformationCallbacks in map(), where(), reduce()
TimerTimer(callback, duration)
Animation completeonComplete()
\\\\n\\\\n
\\\\n

Although the callback pattern is flexible, it can lead to "callback hell" when dealing with multiple layers of asynchronous operations. Dart provides async/await to handle asynchronous flows more elegantly, which we will cover in detail in Chapter 17.

\\\\n
\\\\n\\\\n
\\\\n\\\\n

Dart 3.0 Generic Type Aliases

\\\\n\\\\n

Dart 3.0 extends the capabilities of typedef, so you can now create aliases for any type, not just function types.

\\\\n\\\\n

Example

\\\\n\\\\n
// Dart 3.0: typedef can create aliases for any type\\\\n\\\\n// Aliases for complex collection types\\\\ntypedef JsonMap = Map;\\\\ntypedef UserList = List>;\\\\n\\\\n// Generic alias\\\\ntypedef Result = ({T data, String? error});\\\\n\\\\n// Function type alias (traditional usage)\\\\ntypedef Validator = String? Function(T value);\\\\n\\\\n// Use type aliases\\\\nvoid processJson(JsonMap json) {\\\\n  print('Processing JSON: $json');\\\\n  print('Number of keys: ${json.length}');\\\\n}\\\\n\\\\nvoid validateAndPrint(T value, Validator validator) {\\\\n  var error = validator(value);\\\\n  if (error != null) {\\\\n    print('Validation Failure: $error');\\\\n  } else {\\\\n    print('Validation passed: $value');\\\\n  }\\\\n}\\\\n\\\\nvoid main() {\\\\n  // Use JsonMap alias\\\\n  JsonMap userData = {\\\\n    'name': 'tutorial',\\\\n    'age': 10,\\\\n    'isVip': true,\\\\n  };\\\\n  processJson(userData);\\\\n\\\\n  // Use Result alias\\\\n  Result successResult = (data: 'Operation Success', error: null);\\\\n  Result errorResult = (data: '', error: 'Network connection timed out');\\\\n\\\\n  print('Success: ${successResult.data}');\\\\n  print('Failure: ${errorResult.error}');\\\\n\\\\n  // Use generic Validator\\\\n  Validator nameValidator = (value) {\\\\n    if (value.isEmpty) return 'Name cannot be empty';\\\\n    if (value.length < 3) return 'Name must be at least 3 characters';\\\\n    return null; // null means validation passed\\\\n  };\\\\n\\\\n  validateAndPrint('TUTORIAL', nameValidator);\\\\n  validateAndPrint('AB', nameValidator);\\\\n}\\\\n
\\\\n\\\\n
Processing JSON: {name: tutorial, age: 10, isVip: true}\\\\nNumber of keys: 3\\\\nSuccess: Operation Success\\\\nFailure: Network connection timed out\\\\nValidation passed: TUTORIAL\\\\nValidation Failure: Name must be at least 3 characters
\\\\n\\\\n

Dart 3.0 type aliases greatly reduce verbose type declarations, making code more concise and readable.

\\\\n\\\\n
\\\\n

Type aliases (typedef) and the types themselves are completely equivalent at runtime; they are just "nicknames" at compile time. This means you cannot use typedef to distinguish between two types with the same structure but different semanticsβ€”they will be treated as the same type.

\\\\n
← Dart StreamsDart Exception Handling β†’