こんにちは!FlutterやDartの開発現場で、日夜コードと向き合っている先輩エンジニアです。
今回は、Dart 3で導入された「パターンマッチング(`if-case`)」をテーマに、変数宣言と型ガードの極意を一緒に紐解いていきましょう。
他の言語(TypeScriptのType GuardやSwiftの`if let`など)からDartに入ってきた方なら、「これこれ、こういうのが欲しかったんだよ!」と感動する機能です。ここをクリアすれば、あなたのDartコードは一気にモダンで堅牢になりますよ。
—
1. 従来の「型チェック&キャスト」が抱えていたモヤモヤ
まずは、Dart 3以前(あるいは他の多くの言語)でよく見られた、こんなコードを思い出してみてください。
// サーバーから受け取った型が不明なデータ(JSONなど)
Object rawData = “Hello, Dart!”;
void processData(Object data) {
// 1. まず型チェックをする
if (data is String) {
// 2. チェックしたあとに、わざわざ「as」でキャストして変数に入れ直す
String text = data as String;
print(text.toUpperCase());
}
}
なんだか同じ「String」という型を何度も書いていて、冗長だなと感じませんか?
「`is`で判定したんだから、コンパイラはその中身がStringだって分かっているはずなのに!」というモヤモヤがありますよね。
—
2. Dart 3の `if-case` がもたらす革命
Dart 3では、この「型チェック」と「変数への束縛(宣言と分解)」を同時に行える `if-case` が導入されました。
まずは基本的な書き方を見てみましょう。
void processData(Object data) {
// if (対象 case パターン)
if (data case String text) {
// このブロックの中では、textは確実にString型として扱える!
print(text.toUpperCase());
}
}
これ、すごくスッキリしていませんか?やっていることはこうです。
1. `data` が `String` 型にマッチするか(型ガード)を検証する。
2. マッチした場合、その値を自動的に `text` という新しい変数に代入(分解・束縛)する。
3. `if` ブロックのスコープ内だけで `text` を安全に利用できる。
脳内イメージ:門番と荷物の仕分け
[ Object data ] ──> ( if-case String text )
│
┌─────────────┴─────────────┐
【マッチ成功】 【マッチ失敗】
text = “Hello…” スルー(安全に無視)
ブロックへ突入!
`if-case` は、ただの「条件分岐」ではなく、「構造を解体しながら型を確定させるスマートな門番」なんです。
—
3. 実践!JSONのパースとデータ構造の分解
もう少し実用的な例を見てみましょう。例えば、APIから受け取った動的なマップ(Map)を安全に処理したい場面です。
void handleApiResponse(Object response) {
// レスポンスが「ステータス200とメッセージ」を持つMap構造であるかを検証
if (response case {‘status’: 200, ‘message’: String msg}) {
print(‘成功しました: $msg’);
} else if (response case {‘status’: 400, ‘error’: String err}) {
print(‘クライアントエラー: $err’);
} else {
print(‘未知のレスポンスです’);
}
}
void main() {
handleApiResponse({‘status’: 200, ‘message’: ‘データ取得完了’});
// 出力: 成功しました: データ取得完了
handleApiResponse({‘status’: 400, ‘error’: ‘不正なリクエスト’});
// 出力: クライアントエラー: 不正なリクエスト
}
ここでは、型のチェック(`String msg`)だけでなく、特定の値(`200` や `400`)がそこにあるかまでを `if-case` の中で同時にチェックしています。
従来の `if文` と `Map` のキーアクセスを組み合わせた泥臭いコードを書く必要が一切なくなりますよね。
—
4. 陥りやすい文法エラーと注意ポイント
非常に強力な `if-case` ですが、初学者がハマりがちなポイントがいくつかあります。ここでしっかり押さえておきましょう。
① `if (data is String)` と書き間違える
よくあるミスが、従来の `is` キーワードを混同してしまうケースです。
Object data = “Test”;
// ❌ エラーになる書き方
if (data is String text) { … }
// ⭕ 正しい書き方(isではなく、caseを使う)
if (data case String text) { … }
`is` は単なる真偽値(`true` / `false`)を返す演算子なので、変数を宣言することはできません。変数を同時に宣言・束縛したいときは必ず `case` を使います。
② ガード節(`when`句)の使い所
「型は合っているけれど、さらに細かい条件で絞り込みたい」というときは、`when`(ガード節)を組み合わせます。
void processNumber(Object value) {
if (value case int n when n > 0) {
print(‘正の整数です: $n’);
} else if (value case int n) {
print(‘0または負の整数です: $n’);
}
}
パターンマッチングの条件にさらに一歩踏み込んだビジネスロジックを載せられるため、コードの意図が非常に明確になります。
—
まとめ
今回は Dart 3のパターンマッチングによる「型ガード」と `if-case` について解説しました。
- 従来の `is` チェック + `as` キャストの冗長なコードを排除できる。
- `if (data case Type variable)` の構文で、型チェックと変数への代入を同時に安全に行える。
- データの構造(MapやListなど)や特定の値の検証も一撃で行える。
ここをマスターすると、Dartのコードがぐっとエレガントになり、実行時エラー(TypeCastExceptionなど)を未然に防ぐ堅牢なアプリケーションが作れるようになります。
ぜひ今日のコードから `if-case` を取り入れて、モダンなDartプログラミングを楽しんでくださいね!バッチリマスターしていきましょう!