Open3

Dart 3のsealed classメモ

koji-1009koji-1009

Dart 3のsealed classを試してみたのでメモ。

https://medium.com/dartlang/dart-3-alpha-f1458fb9d232


dart 2 + freezedで書いていたunion。


class LoadParams<PageKey> with _$LoadParams<PageKey> {
  const factory LoadParams.refresh() = _LoadParamsRefresh;

  const factory LoadParams.prepend({
    required PageKey key,
  }) = _LoadParamsPrepend;

  const factory LoadParams.append({
    required PageKey key,
  }) = _LoadParamsAppend;
}

dart 3ではsealed classが利用できる。

sealed class LoadAction<PageKey> {
  const LoadAction._();
}

class Refresh<PageKey> implements LoadAction<PageKey> {
  const Refresh();
}

class Prepend<PageKey> implements LoadAction<PageKey> {
  const Prepend({
    required this.key,
  });

  final PageKey key;
}

class Append<PageKey> implements LoadAction<PageKey> {
  const Append({
    required this.key,
  });

  final PageKey key;
}

unionを利用するには、switchが使える。

dart 2 + freezedの場合のコード。


Future<LoadResult<int, Repository>> load(LoadParams<int> params) async {
  return params.when(
    refresh: () async {
      final data = await repository.repositories();
      return LoadResult.success(
        page: data,
      );
    },
    prepend: (_) {
      return const LoadResult.none();
    },
    append: (key) async {
      final data = await repository.repositories(
        since: key,
      );
      return LoadResult.success(
        page: data,
      );
    },
  );
}

dart 3のsealed classでは、次の2つの書き方ができる様子。
なおLoadResultも同様に置き換えている。


Future<LoadResult<int, Repository>> load(LoadAction<int> params) async {
  switch (params) {
    case Refresh():
      await fetch(null);
      break;
    case Prepend(key: final _):
      const None();
      break;
    case Append(key: final key):
      await fetch(key);
      break;
  }
}

=> で繋ぐと、 {} が利用できない。


Future<LoadResult<int, Repository>> load(LoadAction<int> params) async =>
    switch (params) {
      Refresh() => await fetch(null),
      Prepend(key: final _) => const None(),
      Append(key: final key) => await fetch(key),
    };

Future<LoadResult<int, Repository>> fetch(int? key) async {
  final PageData<int, Repository> data;
  if (key == null) {
    data = await repository.repositories();
  } else {
    data = await repository.repositories(
      since: key,
    );
  }
  return Success(
    page: data,
  );
}
koji-1009koji-1009

switchを条件分岐で使う(case)場合と、式として使う(final result = switch)場合で記述方法が変わるらしい。

switch (result) {
  case Success(page: final page):
      _manager.append(page);
      break;
  case Failure(e: final e):
      _manager.setError(e);
      break;
  case None():
      _manager.append(null);
      break;
}
final _ = switch (result) {
  Success(page: final page) => _manager.append(page),
  Failure(e: final e) => _manager.setError(e),
  None() => _manager.append(null),
};

ただしvoidfinal _で受け取ろうとすると、次のコンパイルエラーがでる。

Error: This expression has type 'void' and can't be used.

koji-1009koji-1009

Dart 3でbreakが必須でなくなったので、次のように書いた方がシンプル。

switch (result) {
  case Success(page: final page):
      _manager.append(page);
  case Failure(e: final e):
      _manager.setError(e);
  case None():
      _manager.append(null);
}