dart-modern-features について
Dart 3.0 以降を対象とする開発者向けに、レコード、拡張型、クラス修飾子などのモダンな機能を案内するスキルです。古い SDK 制約や複雑な公開 API と相性の悪い変更を避ける条件も説明します。
主な機能
- 01レコードで複数の値を返す
- 02クラス修飾子でサブタイプを制限
- 03拡張型によるゼロコストのラッパー
- 04桁区切りとワイルドカードの利用
- 05null 対応要素とドット省略記法
活用シーン
- 01複数の戻り値をレコードにまとめる
- 02クラスの継承境界を明示する
- 03ドメイン値に型安全なラッパーを追加
- 04Dart 3 の条件を満たすコードのモダン化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ kevmoo/ dash_skills --skill dart-modern-featuresインストール後、Claude Code で/dart-modern-featuresを入力して読み込まれたか確認します。
同梱ファイルevals/evals.json
詳細
Dart のモダンな機能
1. このスキルを使用するタイミング
次の場合に、このスキルを使用します。
- Dart 3.0 以降を対象とする Dart コードを記述またはレビューするとき。
- レコード、拡張型、クラス修飾子、null 対応コレクション要素、ドット省略記法、桁区切り文字など、モダンで簡潔かつゼロコストの機能を使用するよう、レガシーな Dart コードをリファクタリングするとき。
- 複数の戻り値やゼロコストのドメインラッパーを扱う、慣用的な方法を探すとき。
パターンマッチング、switch 式、リスト/マップ/分割の分割代入には、
dart-use-pattern-matching を使用してください。閉じたクラス階層を網羅的な sealed 型に変換するには、dart-seal-type-hierarchies を使用してください。
使用しない場合(適用を控えるためのガードレール)
次の場合は、モダンな機能を適用したり、コードをリファクタリングしたりしないでください。
- SDK の制約が 3.0.0 未満の場合:パッケージの
pubspec.yamlで、Dart 2.x をサポートする SDK の制約が指定されている場合(例:sdk: '>=2.19.0 <4.0.0')。 Dart 3 の機能を使うようにリファクタリングすると、Dart 2 のユーザーに構文エラーが発生します。 - 公開 API のレコードにフィールドが 3 つを超える場合:レコードは 2~3 個の戻り値や内部タプルには適していますが、複雑な公開 API のペイロードには、専用のクラスや拡張型のほうが明確で拡張しやすくなります。
調査
モダナイズ候補を見つけるには、次のようにします。
null 対応要素
null をチェックするコレクションの if 文を検索します。
- 正規表現:
if\s*\(\w+\s*!=\s*null\)\s*\w+
桁区切り文字
区切り文字のない長い数値を検索します。
- 正規表現:
\b\d{6,}\b(6 桁以上の数値に一致します)。
2. 機能
レコード
カスタムクラスを定義せずに複数のオブジェクトをまとめる、名前のない不変の集約構造としてレコードを使用します。関数から複数の値を返す場合や、関連するデータを一時的にまとめる場合に使用してください。
避ける: 複数の値を返すだけの単純な用途に、専用クラスを作成すること。
class UserResult {
final String name;
final int age;
UserResult(this.name, this.age);
}
UserResult fetchUser() {
return UserResult('Alice', 42);
}
推奨: レコードを使って、その場で型をシームレスにまとめること。
(String, int) fetchUser() {
return ('Alice', 42);
}
void main() {
var user = fetchUser();
print(user.$1); // Alice
}
クラス修飾子
クラス修飾子(final、base、interface、sealed)を使い、定義元ライブラリの外部でクラスをサブタイプ化する方法を制限します。
interface class:外部ライブラリではimplementできますが、extendはできません。base class:外部ライブラリではextendできますが、implementはできません(非公開の実装不変条件を維持します)。final class:外部ライブラリではextendもimplementもできません。sealed class:同じライブラリ内にサブタイプの閉じたファミリーを作成し、網羅的な switch を可能にします(dart-seal-type-hierarchies を参照)。
避ける: 不変条件を守る必要がある場合に、内部実装クラスを外部から任意にサブクラス化したり、インターフェースを実装したりできる状態にしておくこと。
class TokenStore {
void save(String token) {}
}
推奨: クラス修飾子を使って、許可するサブタイプ化の方法を明示すること。
final class TokenStore {
void save(String token) {}
}
拡張型
既存の型をゼロコストでラップするには、拡張型を使用します。実行時のオーバーヘッドなしに、操作を制限したり、独自の振る舞いを追加したりするために使います。
避ける: ドメイン固有のロジックや型安全性のためだけに、新しいラッパーオブジェクトを割り当てること。
class Id {
final int value;
Id(this.value);
bool get isValid => value > 0;
}
推奨: 実行時に基底の型へコンパイルされる拡張型を使用すること。
extension type Id(int value) {
bool get isValid => value > 0;
}
桁区切り文字
大きな数値を視覚的に読みやすくする目的に限り、数値リテラルにアンダースコア(_)を使用します。
避ける: 一目で読み取りにくい、長い数値リテラル。
const int oneMillion = 1000000;
推奨: アンダースコアを使って、千の位やその他の桁区切りを入れること。
const int oneMillion = 1_000_000;
ワイルドカード変数
値を意図的に使用しないことを明示するには、ワイルドカード(_)を非バインド変数またはパラメーターとして使用します。
避ける: 「未使用の変数」に関する警告を避けるために、扱いにくい固有の変数名を考案すること。
void handleEvent(String ignoredName, int status) {
print('Status: $status');
}
推奨: アンダースコアを使って、バインディングを明示的に破棄すること。
void handleEvent(String _, int status) {
print('Status: $status');
}
Null を考慮した要素
コレクションリテラル内で Null を考慮した要素(?)を使うと、Null でない値として評価された場合にのみ項目を含められます。
避ける: 単純な Null チェックにコレクションの if 文を使うこと。
var names = [
'Alice',
if (optionalName != null) optionalName,
'Charlie'
];
推奨: ? プレフィックスをインラインで使うこと。
var names = ['Alice', ?optionalName, 'Charlie'];
ドット省略記法
列挙型や静的フィールドなど、コンテキストから型を確実に推論できる場合は、ドット省略記法を使って明示的な型名を省略します。
避ける: コンテキストから型が明らかな場合に、型名を完全修飾すること。
LogLevel currentLevel = LogLevel.info;
推奨: 推論による省略記法で視覚的なノイズを減らすこと。
LogLevel currentLevel = .info;
関連スキル
- dart-use-pattern-matching: Dart 3 のパターンマッチング、switch 式、リスト/マップ/
String.split()の分割に関する決定版ガイドです。 - dart-seal-type-hierarchies: クローズドなクラス階層を、網羅的な switch に対応する
sealed型に変換します。 - dart-best-practices: モダンな構文機能の登場前からある、またはそれを補完する一般的なコードスタイルと Dart の基本的なイディオムです。