Dart - Using Type Alias (Typedef) Examples

This tutorial shows you how to create and use type aliases, also known as typedefs in Dart. It also works in Flutter and there's a usage example for Flutter as well.

In Dart, you can create a type alias to refer to a type by using typedef keyword. This tutorial explains how to create typedefs for function and non-functions and how to use the created typedefs.

Using typedef for Functions

The typedef keyword was initially created in Dart 1 to refer to functions. In Dart 1, if you want to use a function as a variable, field, or parameter, you need to create a typedef first.

To use a type alias, you only need to assign the function signature to a typedef. After that, you can use the typedef as a variable, field, or parameter, as shown in the example below.

  typedef IntOperation<int> = int Function(int a, int b);
  
  int processTwoInts (IntOperation<int> intOperation, int a, int b) {
    return intOperation(a, b);
  }
  
  class MyClass {
  
    IntOperation<int> intOperation;
  
    MyClass(this.intOperation);
  
    int doIntOperation(int a, int b) {
      return this.intOperation(a, b);
    }
  }
  
  void main() {
    IntOperation<int> sumTwoNumbers = (int a, int b) => a + b;
    print(sumTwoNumbers(1, 1));
  
    print(processTwoInts(sumTwoNumbers, 1, 2));
  
    MyClass myClass = MyClass(sumTwoNumbers);
    print(myClass.doIntOperation(3, 4));
  }

Output:

  2
  3
  7

Below is another example where the function has a generic parameter type.

  typedef Compare<T> = bool Function(T a, T b);
  bool compareAsc(int a, int b) => a < b;
  int compareAsc2(int a, int b) => a - b;
  
  bool doComparison<T>(Compare<T> compare, T a, T b) {
    assert(compare is Compare<T>);
    return compare(a, b);
  }
  
  void main() {
    print(compareAsc is Compare<int>);
    print(compareAsc2 is Compare<int>);
  
    doComparison(compareAsc, 1, 2);
    doComparison(compareAsc2, 1, 2); // The argument can't be assigned
  }

Output:

  true
  false
  true

Since Dart 2, you can use function type syntax anywhere. Therefore, it's not necessary to create a typdef anymore. Flutter also states that inline function type is preferred. That's because people who read the code can see the function type directly. Below is the equivalent of the first example without typedef.

  int processTwoInts (int Function(int a, int b) intOperation, int a, int b) {
    return intOperation(a, b);
  }
  
  class MyClass {
  
    int Function(int a, int b) intOperation;
  
    MyClass(this.intOperation);
  
    int doIntOperation(int a, int b) {
      return this.intOperation(a, b);
    }
  }
  
  void main() {
    int Function(int a, int b) sumTwoNumbers = (int a, int b) => a + b;
    print(sumTwoNumbers(1, 1));
  
    print(processTwoInts(sumTwoNumbers, 1, 2));
  
    MyClass myClass = MyClass(sumTwoNumbers);
    print(myClass.doIntOperation(3, 4));
  }

However, it can be useful to create a typedef if the function is long and frequently used.

Using typedef for Non-Functions

Before Dart 2.13, you can only use typedefs for function types. Since Dart 2.13, you can also use typedefs for creating type aliases that refer to non-functions. The usage is very similar, you only need to assign the type to be referred as a typedef.

First, your Dart version must be version 2.13 or above. For Flutter, you need to use version 2.2 or above. You also need to update the minimum SDK version in pubspec.yaml to 2.13.0 and run pub get (for Dart) or flutter pub get (for Flutter).

  environment:
    sdk: '>=2.13.0 <3.0.0'

For example, you need to define a type that stores a list of integer scores. For that purpose, you can create a typedef whose type is List<int>. Later, if you want to define a variable for storing a score list, you can use the typedef as the type. In the example below, we define a typedef called ScoreList whose type is List<int>. As you can see in the code below, using the typedef gives you the same behavior as using the actual type. You can directly assign a list value and access the methods and properties of List. If you check the runtimeType, you'll get List<int> as the result.

  typedef ScoreList = List<int>;
  
  void main() {
    ScoreList scores = [60, 80, 70];
    scores.add(100);
    print('length: ${scores.length}');
    print('values: $scores');
    print('type: ${scores.runtimeType}');
  }

Output:

  length: 4
  values: [60, 80, 70, 100]
  type: List<int>

Not only as a variable, type aliases can also be used as a field, parameter, and return value of a method.

  typedef ScoreList = List<int>;
  
  class MyClass {
  
    ScoreList currentScores;
  
    MyClass({required this.currentScores});
  
    set scores(ScoreList currentScores) {
      this.currentScores = currentScores;
    }
  
    ScoreList getMultipliedScores(int multiplyFactor) {
      ScoreList result = [];
  
      currentScores.forEach((element) {
        result.add(element * multiplyFactor);
      });
  
      return result;
    }
  }
  
  void main() {
    MyClass myClass = MyClass(currentScores: [60, 80, 70]);
  
    myClass.scores = [70, 90];
    print(myClass.currentScores);
  
    print(myClass.getMultipliedScores(2));
  }

Output:

  [70, 90]
  [140, 180]

Below is another example. For example, you need a type for storing request body whose keys and value types can be vary for each type. The Map<String, dynamic> data type is suitable for that case. But instead of using Map<String, dynamic> every time you need to declare a request body variable, you can create a typedef for the type.

  typedef RequestBody = Map<String, dynamic>;
  
  void main() {
    final RequestBody requestBody1 = {
      'type': 'PURCHASE',
      'itemId': 1,
      'amount': 100,
    };
    final RequestBody requestBody2 = {
      'type': 'CANCEL_PURCHASE',
      'orderId': '04184432',
    };
  
    print(requestBody1);
    print(requestBody2);
  }

Output:

  {type: PURCHASE, itemId: 1, amount: 100}
  {type: CANCEL_PURCHASE, orderId: 04184432}

You can also define a type alias that has a generic type parameter. The ValueList type alias below has a generic type parameter T. When you define a variable using the type alias, you can pass the generic type to use.

  typedef ValueList<T> = List<T>;
  
  void main() {
    ValueList<String> values = ['a', 'b', 'c'];
    values.add('d');
    print('length: ${values.length}');
    print('values: $values');
    print('type: ${values.runtimeType}');
  }

Output:

  length: 4
  values: [a, b, c, d]
  type: List<String>

Usage in Flutter

The code below is an example for Flutter which creates a typedef for List<Widget> type.

  import 'package:flutter/material.dart';
  
  typedef WidgetList = List<Widget>;
  
  void main() => runApp(MyApp());
  
  class MyApp extends StatelessWidget {
  
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Woolha.com Flutter Tutorial',
        home: TypedefExample(),
        debugShowCheckedModeBanner: false,
      );
    }
  }
  
  class TypedefExample extends StatelessWidget {
  
    WidgetList buildWidgets() {
      return <Widget>[
        const FlutterLogo(size: 60),
        const Text('Woolha.com', style: const TextStyle(color: Colors.teal, fontSize: 24)),
      ];
    }
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Woolha.com Flutter Tutorial'),
        ),
        body: SizedBox(
          width: double.infinity,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: buildWidgets(),
          ),
        ),
      );
    }
  }

 

Summary

That's how to create and use typedefs in Dart / Flutter. You need to assign a type or a function signature to a typedef. Then, the created typedef can be used as a variable, field, parameter, or return value of a method.