0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Dart 3.3で導入された extension type について考えてみます。
extension type は、既存の型に対して新しい型を定義し、特定の制約や振る舞いを追加できる機能です。
これにより、コードの安全性と可読性が向上します。

使い方を考えてみる

extension type の使い方を4つほど考えてみました。

1. 限定された値の集合を表す型

特定の値の集合のみを許容する型を定義したい場合に便利です。

extension type ID._(String id) {
  static const _allowedIds = {'A0001', 'B0001', 'C0002', 'D1001'};

  factory ID(String value) {
    if (!_allowedIds.contains(value)) {
      throw Exception('Invalid ID: $value');
    }

    return ID._(value);
  }
}

main() {
  // OK
  ID("A0001");

  // throw Exception
  ID("NGID");
}

2. APIの制限・機能の隠蔽

既存のクラス(特に List や Map などのコレクション)の機能をあえて隠し、特定の操作だけを許可したい場合に最適です。

例えば、「追加はできるが、削除はできないリスト(ログ用など)」や「スタック構造」を作る場合、通常のクラスでラップするとメモリを余分に消費しますが、extension type ならゼロコストです。

// Listをラップしているが、Listのメソッド(add, removeなど)は直接見せない
extension type LogBuffer(List<String> _list) {
  // 必要な操作だけを公開する
  void addLog(String message) {
    _list.add('${DateTime.now()}: $message');
  }

  // 読み取り専用のビューだけ返すとか
  List<String> get entries => List.unmodifiable(_list);

  int get count => _list.length;
}

void main() {
  final logs = LogBuffer([]);
  logs.addLog("Start");
  logs.addLog("Processing");

  // Listのメソッドは隠蔽されているため、コンパイルエラー
  logs.removeAt(0);

  print(logs.entries);
}

3. JSON/Map の型安全なラッパー

APIレスポンスなどの Map<String, dynamic> を扱う際、わざわざ fromJson を持つクラスを定義して全プロパティをコピー(インスタンス生成)するのは、巨大なデータだとコストがかかります。

extension type を使うと、Mapの実体のまま、プロパティアクセスのように見せることができ、構造的型付けのような振る舞いを実現できます。

extension type UserJson(Map<String, dynamic> _json) {
  String get name => _json['name'] as String;
  int get age => _json['age'] as int;

  // ネストしたデータもラップして返すことが可能
  bool get hasEmail => _json.containsKey('email');
}

void main() {
  final apiResponse = {'name': 'Jiro', 'age': 30, 'email': 'jiro@example.com'};

  final user = UserJson(apiResponse);

  print(user.name);
}

Nullableなプロパティも扱えます。

  String? get name => _json['name'] as String?;

4. 単位の取り違え防止

プリミティブ型に意味論的なラベルを貼ります。
例えば、同じ double でも「ピクセル」と「パーセント」や、「幅」と「高さ」を間違えて計算しないようにします。

extension type Px(double value) implements double {
  // implements double をつけると、doubleのメソッド(+,-,*,/など)をそのまま使える

  // Px同士の足し算だけ許可し、Percentとの計算を防ぐような制御も書ける
  Px operator +(Px other) => Px(value + other.value);
}

extension type Percent(double value) {
  // パーセントは 0.0 ~ 1.0 の範囲など、独自の計算ロジックを持たせる
  double toValue(double total) => total * value;
}

void main() {
  final width = Px(100);
  final opacity = Percent(0.5);

  // コンパイルエラーになる
  final result = width + opacity;

  print(width + Px(50)); // OK: 150.0
}

まとめ

extension type は、Dartにおいて型安全性とコードの可読性を向上させる強力なツールです。
特に、限定された値の集合を表す型、APIの制限・機能の隠蔽、JSON/Mapの型安全なラッパー、単位の取り違え防止など、様々なシナリオで役立ちます。
適切に活用することで、より堅牢でメンテナンスしやすいコードを書くことができます。

ぜひ試してみてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?