dart-language-syntax について
Dart 開発者やコーディングエージェント向けのスキルです。状態管理、関数、レコード、パターン、ジェネリクスを通じて、慣用的な Dart の記述と近代化を支援します。
主な機能
- 01var、final、const の使い分け
- 02ティアオフとジェネレーターの活用
- 03レコードによる複数値の返却と分解
- 04パターンと switch 式の活用
- 05型安全なジェネリックコードの作成
活用シーン
- 01既存の Dart コードの近代化
- 02タプルクラスをレコードに置き換え
- 03パターンによる JSON の検証
- 04switch 式による条件分岐のリファクタリング
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dart-lang/ skills --skill dart-language-syntaxインストール後、Claude Code で/dart-language-syntaxを入力して読み込まれたか確認します。
詳細
慣用的な Dart の書き方
目次
変数と状態管理
厳格な可変性と型推論のルールに従って、状態と変数宣言を管理します。
varを優先する: 代入される型が明らかな場合は、ローカル変数にvarを使用します(例:var name = 'Bob';)。初期化式から型がすぐに明らかにならない場合は、明示的な型を使用します。- 不変性を徹底する: 初期化後に再代入しない変数には
finalを使用します。コンパイル時定数や、正準化された不変のオブジェクトインスタンスを作成する場合はconstを使用します。 lateを活用する:late修飾子を使うと、null 非許容変数の初期化を初回使用時まで遅らせられます。特に、計算コストが高い場合や、初期化にthisへのアクセスが必要な場合に有効です。- ワイルドカードを使用する: ローカル宣言、クロージャ、パターンマッチングで未使用の値を破棄するには、ワイルドカード変数
_(Dart 3.7 以降が必要)を使用します。これにより、未使用変数の警告を回避できます。
関数とクロージャ
合成可能性を最大限に高め、ボイラープレートを最小限に抑えるよう関数を構成します。
- アロー構文を使用する:
=>演算子を使って、単一式の関数を簡潔に記述します。 - ティアオフを優先する: 関数参照(ティアオフ)を、冗長な匿名クロージャでラップせずに直接渡します(例:
list.forEach((e) => print(e))の代わりにlist.forEach(print)を使用します)。 - ジェネレーターを実装する:
sync*を使ってIterableシーケンスを遅延生成し、async*を使ってStreamシーケンスを生成します。yieldで値を生成するか、yield*で他のジェネレーターに処理を委譲します。 - 名前付きパラメーターを定義する: 呼び出し箇所の可読性を高めるため、真偽値のフラグや複数の引数を持つ関数には、名前付きパラメーター
({required Type name})を使用します。
レコードとパターンマッチング
レコードとパターンを使って、ボイラープレートのデータクラスや複雑な条件分岐をなくします。
- 複数の値を返す: 専用のクラスを定義せずに関数から複数の値を返すには、レコード
(Type, Type)を使用します。 - 代入を分解する: パターンマッチングを活用して、レコード、リスト、オブジェクトをローカル変数に直接分解します。
- switch 式を使用する: 複雑な
if-elseの連鎖を switch 式に置き換えます。論理和パターン(||)やガード節(when)を活用して、ケース間でロジックを共有します。 - JSON を宣言的に検証する: マップパターンとリストパターンを使って、動的な JSON ペイロードの構造を検証し、型を確認してデータを抽出します。
ジェネリクスと型安全性
コレクションやカスタムコンポーネント全体で、型安全性と再利用性を確保します。
- ジェネリクスを実装する: クラス、メソッド、コレクションをパラメーター化するには
<T>を使用します。 - 型パラメーターを制約する: ジェネリック型に上限を設けるには
extendsを使用します(例:null 非許容性を強制する<T extends Object>や、<T extends BaseWidget>)。 - F境界を使用する: クラスが自身と完全に同じ型のインスタンスとやり取りする必要がある場合は、自己参照型の制約を実装します(例:
class Node<T extends Node<T>>)。 - 機能を拡張する: サブクラス化せずに、拡張メソッドや拡張型を使って、既存のジェネリッククラスや具象クラスにユーティリティ関数を追加します。
ワークフロー:慣用的な Dart へのリファクタリング
このチェックリストを使って、レガシーな Dart コードを最新の慣用的な Dart 3 以降の標準にアップグレードします。
- タスクの進捗:変数の最新化
- 明らかなローカル代入では、明示的な型を
varに置き換えます。 - 再代入されない可変な
var宣言をfinalに変更します。 - 未使用のパラメーターに使われている
__または___を、標準の_ワイルドカードに置き換えます。
- 明らかなローカル代入では、明示的な型を
- タスクの進捗:制御フローと戻り値
- 独自の「Tuple」または「Pair」クラスを、ネイティブの Record
(T1, T2)に置き換えます。 - 冗長なクロージャを関数またはメソッドのティアオフにリファクタリングします。
- 1 つの文だけで構成される関数本体を、アロー構文
=>に変更します。
- 独自の「Tuple」または「Pair」クラスを、ネイティブの Record
- タスクの進捗:パターンマッチングの導入
- オブジェクトから複数のフィールドを取り出す場合は、オブジェクトの分割代入を使います:
var User(:name, :age) = user;。 - ネストされた JSON を検証する場合は、
isチェックと手動のキャストをマップ/リストパターンに置き換えます。 - 代数的データ型(シールドクラス)を切り替える場合は、
switch文を網羅的なswitch式に変更します。
- オブジェクトから複数のフィールドを取り出す場合は、オブジェクトの分割代入を使います:
- バリデーターの実行 → エラーの確認 → 修正:
dart analyzeとdart formatを実行します。型の昇格または網羅性に関する lint エラーを解決します。
例
複数の戻り値の分割代入(Records)
入力(レガシー):
class UserInfo {
final String name;
final int age;
UserInfo(this.name, this.age);
}
UserInfo fetchUser() => UserInfo('Dash', 10);
final info = fetchUser();
print(info.name);
出力(慣用的な書き方):
(String, int) fetchUser() => ('Dash', 10);
// Destructure directly into final variables
final (name, age) = fetchUser();
print(name);
宣言的な JSON 検証(パターン)
入力(レガシー):
if (json is Map<String, Object?> && json.containsKey('user')) {
var user = json['user'];
if (user is List<Object> && user.length == 2 && user[0] is String && user[1] is int) {
var name = user[0] as String;
var age = user[1] as int;
print('User $name is $age');
}
}
出力(慣用的な書き方):
if (json case {'user': [String name, int age]}) {
print('User $name is $age');
}
Switch 式とガード節
実装:
sealed class Shape {}
class Square implements Shape { final double length; Square(this.length); }
class Circle implements Shape { final double radius; Circle(this.radius); }
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) when l > 0 => l * l,
Circle(:var radius) when radius > 0 => 3.14159 * radius * radius,
_ => 0.0, // Fallback for invalid dimensions
};
ジェネリクスを使った拡張メソッド
実装:
extension IterableExtensions<T> on Iterable<T> {
/// Returns the first element matching the predicate, or null.
T? firstWhereOrNull(bool Function(T) test) {
for (final element in this) {
if (test(element)) return element;
}
return null;
}
}
コメント
Markdown 対応