К основному содержанию
скилл Claude Code CX Codex Cursor GitHub Copilot

dart-use-pattern-matching — скилл для Claude Code: паттерны Dart 3

Скилл задаёт агенту правила работы с паттернами Dart 3: switch-выражения, деструктуризация JSON, sealed-классы, сегменты пути.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add dart-lang/skills --skill dart-use-pattern-matching -a claude-code
Открыть на GitHub

Что делает dart-use-pattern-matching

Скилл задаёт агенту правила работы с паттернами Dart 3. Внутри — разделы про выбор типа паттерна под структуру данных, разницу между switch-выражением и switch-инструкцией, синтаксис логических, реляционных, list, map, record и object-паттернов.

Перед правкой кода агент сверяется с таблицей условий. Полиморфный JSON разбирается через switch по ключу-дискриминатору в sealed-иерархию, сегменты uri.pathSegments и результаты String.split() — через list-паттерны с rest-элементом, несколько возвращаемых значений — через record-паттерны. Ручные проверки .length и арифметика индексов после этого уходят.

Самое полезное здесь — раздел про антипаттерны. Битые данные глушить нельзя: если элемент должен ронять разбор, if-case в цикле не подходит, нужна явная ошибка. Для проверки одного типа скилл советует is, а не if-case с лишним алиасом. Всё это работает на Dart 3, так что проверьте версию SDK в проекте перед тем, как просить агента переписать разбор ответа API.

Какие задачи решает

  • разобрать полиморфный JSON в sealed-иерархию через switch по дискриминатору
  • переписать длинную цепочку if-else на switch-выражение
  • разложить сегменты uri.pathSegments без арифметики индексов
  • достать несколько полей MapEntry в локальные переменные
  • добиться полноты switch на sealed-классах

Примеры запросов агенту

  • Разбери этот ответ API через switch-выражение и sealed-классы, скилл dart-use-pattern-matching.
  • Перепиши разбор `uri.pathSegments` на list-паттерны с rest вместо проверок `.length`.
  • Проверь мой `switch` на полноту по sealed-классу и убери лишние `null`-ветки.

Как установить dart-use-pattern-matching

1Через Skills CLI (Vercel)

npx skills add dart-lang/skills --skill dart-use-pattern-matching -a claude-code

2Вручную: положить файлы

~/.claude/skills/dart-use-pattern-matching/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/dart-use-pattern-matching/

Из того же набора7

Весь набор →
dart-add-unit-test dart-lang Пишет unit-тесты на Dart и Flutter через package:test: раскладывает файлы по test/, добавляет моки и гоняет прогон до зелёного. Claude Code CX Codex Cursor +1 19 100установок dart-run-static-analysis dart-lang Запускает dart analyze для поиска ошибок и dart fix --apply для автоисправления предупреждений линтера. Помогает проверять качество кода на Dart перед коммитами. Claude Code CX Codex Cursor +1 19 000установок dart-resolve-package-conflicts dart-lang Скилл для решения конфликтов версий пакетов в Dart-проектах, когда pub get не может найти совместимые версии. Помогает разобраться с lockfile и обновить зависимости без полного пересчёта. Claude Code CX Codex Cursor +1 18 600установок dart-generate-test-mocks dart-lang Скилл для юнит-тестов на Dart: агент выносит внешние зависимости в конструктор, генерирует моки через mockito и build_runner, потом пишет тесты. Claude Code CX Codex Cursor +1 18 200установок dart-build-cli-app dart-lang Скилл для разработки command-line приложений на Dart. Даёт паттерны архитектуры, структуру точки входа, коды выхода, маршрутизацию потоков и запуск подпроцессов. Claude Code CX Codex Cursor +1 18 000установок dart-migrate-to-checks-package dart-lang Переводит тесты Dart с устаревшего пакета package:matcher на package:checks. Пригодится для перехода на строгую типизацию утверждений и улучшение диагностики ошибок. Claude Code CX Codex Cursor +1 18 000установок

Похожие

dwarf-expert trailofbits Разбирает отладочные секции DWARF в бинарниках, проверяет целостность деревьев DIE и помогает писать парсеры. Пригодится при анализе скомпилированного кода. Claude Code CX Codex Cursor +1 5 200установок rust-skills leonardomso Справочник по Rust для агента: 265 правил в 26 категориях. Пригодится, когда пишете функцию, ревьюите модуль или воюете с заимствованием. Claude Code CX Codex Cursor +1 6 400установок migrate-to-shoehorn Matt Pocock Переводит type assertions с as в тестах на функции из @total-typescript/shoehorn. Нужен для частичных объектов и намеренно неверных данных в проверках. Claude Code 475 388установок write-swift emilkowalski Скилл помогает писать, проверять и переносить код на Swift с учётом модели языка. Подойдёт, когда нужно разобраться с конкурентностью, ошибкой или циклом удержания объектов. Claude Code CX Codex Cursor +1 117 500установок megacave JuliusBrussee Переводит ответы ассистента на классический китайский язык (вэньянь) для экономии места на экране. Активируется командами /megacave или /caveman wenyan. Claude Code CX Codex Cursor +1 24 400установок find-docs upstash Тянет актуальную документацию, справочники API и примеры кода для любой библиотеки через Context7 CLI. Пригодится, когда агент отвечает по устаревшим данным обучения. Claude Code CX Codex Cursor +1 9 400установок typescript-advanced-types Seth Hobson Помогает агенту писать сложную типизацию TypeScript: generics, conditional types и преобразования типов. Нужен для библиотек, API-клиентов и строгих конфигураций. Claude Code CX Codex Cursor 92 169установок smart-explore thedotmack Структурно ищет функции и классы через tree-sitter. Пригодится, когда нужно разобраться в кодовой базе без чтения всех файлов. Claude Code CX Codex Cursor +1 7 300установок

Вопросы о dart-use-pattern-matching

Как установить dart-use-pattern-matching в Claude Code?

Выполните npx skills add dart-lang/skills --skill dart-use-pattern-matching -a claude-code. Скилл появится по пути ~/.claude/skills/dart-use-pattern-matching/SKILL.md.

Что делает dart-use-pattern-matching?

Описывает, когда и какой паттерн Dart 3 применить: switch-выражения, деструктуризация List, Map, Record, объектные паттерны на sealed-классах. Агент получает готовые правила выбора вместо общих рассуждений о синтаксисе.

Когда скилл не нужен?

Для простых булевых условий, повышения типа одной переменной и обычной фильтрации коллекций: там он прямо советует is вместо if-case.

Исходный текст

SKILL.md · BSD-3-Clause
---
name: dart-use-pattern-matching
description: >-
  Applies Dart 3 pattern matching, switch expressions, and destructuring
  idiomatically to validate data schemas, handle algebraic data types, and
  decompose control flow. Use when refactoring complex if-else chains,
  parsing polymorphic JSON or API responses, destructuring Lists, URI/path
  segments, String.split() tokens, Records, or Maps, or enforcing exhaustiveness
  on sealed classes. Don't use for simple boolean conditions, single-variable
  type promotion (use `is`), or basic collection filtering.
metadata:
  model: models/gemini-3.1-pro-preview
  last_modified: Tue, 13 Oct 2026 19:45:00 GMT
---
# Implementing Dart Patterns

## Contents
- [Pattern Selection Strategy](#pattern-selection-strategy)
- [Switch Statements vs. Expressions](#switch-statements-vs-expressions)
- [Core Pattern Implementations](#core-pattern-implementations)
- [Pragmatic Balance & Anti-Patterns](#pragmatic-balance--anti-patterns)
- [Workflows](#workflows)
- [Examples](#examples)

## Pattern Selection Strategy

Apply specific pattern types based on the data structure and desired outcome. Follow these conditional guidelines:

*   **If validating and extracting from deserialized data (e.g., JSON):** Use Map, List, and Object patterns to validate schema structure and destructure properties in a single step.
*   **If inspecting URL/path segments (`uri.pathSegments`, `p.split(path)`) or `String.split()` tokens:** Use List patterns with rest elements (`['api', 'comments', ...]`, `['assets', ...final rest]`, `[..., final parent, _]`) instead of manual `.length` checks, `.first`, `.skip(1)`, or `length - N` index arithmetic.
*   **If handling polymorphic payloads or responses:** Use `switch` expressions over map discriminant keys to deserialize into `sealed` class hierarchies.
*   **If handling multiple return values:** Use Record patterns to destructure fields directly into local variables.
*   **If unpacking multiple fields from an object or `MapEntry`:** Use Object patterns (`final Spec(:field1, :field2!) = spec;`, `for (final MapEntry(:key, :value) in map.entries)`) when promoting nullable properties or unpacking 3+ fields used across multiple statements, rather than for single-call passthroughs.
*   **If executing type-specific behavior (Algebraic Data Types):** Use Object patterns combined with `sealed` classes to ensure exhaustiveness.
*   **If matching numeric ranges or conditions:** Use Relational (`>=`, `<=`) and Logical-and (`&&`) patterns within switch arms.
*   **If multiple cases share logic:** Use Logical-or (`||`) patterns to share a single case body or guard clause.
*   **If ignoring specific values:** Use the Wildcard pattern (`_`) or a non-matching Rest element (`...`) in collections.

## Switch Statements vs. Expressions

Select the appropriate switch construct based on the execution context:

*   **If producing a value:** Use a **switch expression**.
    *   Syntax: `switch (value) { pattern => expression, }`
    *   Rule: Each case must be a single expression. No implicit fallthrough. Must be exhaustive.
*   **If executing statements or side effects:** Use a **switch statement**.
    *   Syntax: `switch (value) { case pattern: statements; }`
    *   Rule: Empty cases fall through to the next case. Non-empty cases implicitly break (no `break` keyword required).

## Core Pattern Implementations

Implement patterns using the following syntax and rules:

*   **Logical-or (`||`):** `pattern1 || pattern2`. Both branches must define the exact same set of variables.
*   **Logical-and (`&&`):** `pattern1 && pattern2`. Branches must *not* define overlapping variables.
*   **Relational:** `==`, `!=`, `<`, `>`, `<=`, `>=` followed by a constant expression.
*   **Cast (`as`):** `pattern as Type`. Throws if the value does not match the type. Use to forcibly assert types during destructuring.
*   **Null-check (`?`):** `pattern?`. Fails the match if the value is null. Binds the variable to the non-nullable base type.
*   **Null-assert (`!`):** `pattern!`. Throws if the value is null.
*   **Variable:** `var name` or `Type name`. Binds the matched value to a new local variable.
*   **Wildcard (`_`):** Matches any value and discards it.
*   **List:** `[pattern1, pattern2]`. Matches lists of exact length unless a Rest element (`...` or `...var rest`) is used.
*   **Map:** `{"key": pattern}`. Matches maps containing the specified keys. Ignores unmatched keys.
*   **Record:** `(pattern1, named: pattern2)`. Matches records of the exact shape. Use `:var name` to infer the getter name.
*   **Object:** `ClassName(field: pattern)`. Matches instances of `ClassName`. Use `:var field` to infer the getter name.

## Pragmatic Balance & Anti-Patterns

Pattern matching and switch expressions should simplify code, not add syntactic overhead. Observe the following boundaries:

### 1. Prefer `is` Type Promotion over `if-case` for Single Promotable Variables
When checking or promoting a single variable, use standard `is` checks instead of `if-case` patterns that introduce shadow aliases.

*   **Prefer:**
    ```dart
    // ✅ Promotes `key` directly in-place without extra variables
    for (final MapEntry(:key, :value) in map.entries) {
      if (key is String && value != null) {
        process(key, value);
      }
    }
    ```
*   **Avoid:**
    ```dart
    // ❌ Anti-pattern: Introduces unnecessary alias variable `k`
    for (final MapEntry(:key, :value) in map.entries) {
      if (key case final String k when value != null) {
        process(k, value);
      }
    }
    ```

### 2. Consolidate Nullable Types in Switch Arms
When mapping or returning values where both `null` and a type `T` are valid and handled identically, match the nullable type `T?` directly rather than creating redundant `null` arms.

*   **Prefer:**
    ```dart
    // ✅ Clean nullable pattern match
    switch (value) {
      final String? s => s,
      _ => throw FormatException('Invalid value: $value'),
    }
    ```
*   **Avoid:**
    ```dart
    // ❌ Redundant separate null arm
    switch (value) {
      final String s => s,
      null => null,
      _ => throw FormatException('Invalid value: $value'),
    }
    ```

### 3. Preserve Fast-Fail Validation (Do Not Silently Drop Data)
Do not use `if-case` in loops or deserialization to filter elements if malformed data should trigger an error or diagnostic warning.

*   **Prefer:**
    ```dart
    // ✅ Fast-fail with explicit diagnostic error
    for (final raw in rawTasks) {
      if (raw is! Map<String, dynamic>) {
        throw FormatException('Expected Map item, got ${raw.runtimeType}: $raw');
      }
      _applyTask(raw);
    }
    ```
*   **Avoid:**
    ```dart
    // ❌ Silently ignores malformed items
    for (final raw in rawTasks) {
      if (raw case final Map<String, dynamic> taskMap) {
        _applyTask(taskMap);
      }
    }
    ```

### 4. Avoid Single-Case or Boolean Switches
*   Use `if (x is T)` instead of a `switch` statement with only 1 case and `default: break;`.
*   Use standard conditional ternary operators (`condition ? a : b`) instead of `switch (condition) { true => a, false => b }`.

### 5. Use Object Destructuring for Multi-Field Unpacking, Not Single Properties or Passthroughs
Use object or record destructuring when extracting 3+ fields used across multiple statements, iterating `MapEntry(:key, :value)` in `map.entries` loops, eliminating a single-use intermediate variable, or promoting nullable fields to local variables (`:field!` or `:field` with `!= null`). Keep standard property/element access (`config.field`) when:
*   Reading a single property (`user.name`, or `final CpuCallFrame(:url, :line) = node.callFrame;` rather than nesting `ProfileNode(callFrame: ...)`).
*   Unpacking only 1–2 fields in a short getter/method, or unpacking fields only to pass them straight into a single constructor or function call.
*   Explicit local type annotations (`specify_nonobvious_local_variable_types`) require adding new library imports for marginal (1–2x) field reuse.
*   A long class name with renamed getters wraps a 2-field unpack across 3+ lines.
*   Any target variable is assigned to an outer-scoped variable (e.g., `db = res.$1;` before `finally` — `final (db, ...)` shadows outer `db`) or mixes `final` and mutable `var` locals.
*   Reading `.first` and `.last` on a list that may have length 1 (`[first, ..., last]` requires `length >= 2`; do not replace with multi-branch `if-case`).

*   **Prefer:**
    ```dart
    // ✅ Unpacks multiple fields and asserts non-nullability in one declaration
    final RunSpec(:target, :device!, :verbose) = spec;
    ```
*   **Avoid:**
    ```dart
    // ❌ Repetitive multi-line property extraction
    final target = spec.target;
    final device = spec.device!;
    final verbose = spec.verbose;
    ```

### 6. Avoid `if-case` for Standalone Scalar Comparisons
Use standard boolean operators (`if (code >= 200 && code < 300)`) instead of `if (code case >= 200 && < 300)` for standalone conditions. Reserve relational patterns for multi-arm `switch` tables.

## Workflows

### Task Progress: Implementing Pattern Matching
Copy this checklist to track progress when implementing complex pattern matching logic:

- [ ] Identify the data structure being evaluated (JSON, List/Segments, Record, Class, Enum).
- [ ] Select the appropriate switch construct (Expression for values, Statement for side-effects).
- [ ] Define the required patterns (Object, Map, List, Record).
- [ ] Extract required data using Variable patterns (`var x`, `:var y`, `...final rest`).
- [ ] Apply Guard clauses (`when condition`) for logic that cannot be expressed via patterns.
- [ ] Handle unmatched cases using a Wildcard (`_`) or `default` clause (if not using a sealed class).
- [ ] Run static analyzer for exhaustiveness and dead code (`dart analyze`).
- [ ] Verify runtime Map/JSON pattern behavior on omitted keys (`containsKey` semantics) vs explicit `null` values.

### Feedback Loop 1: Exhaustiveness Checking (Static Verification)
When switching over `sealed` classes or enums, ensure all subtypes are handled at compile time:

1. **Run analyzer:** Execute `dart analyze`.
2. **Review errors:** Look for "The type 'X' is not exhaustively matched by the switch cases" or unreachable pattern arm warnings.
3. **Fix:** Add the missing Object patterns for unhandled subtypes, or add an explicit wildcard (`_`) arm if a default fallback or error is acceptable.

### Feedback Loop 2: Runtime Map & JSON Pattern Verification
Because `dart analyze` cannot statically verify dynamic `Map<String, dynamic>` keys, validate runtime pattern semantics explicitly:

1. **Omitted Keys vs. Explicit `null`**: A map pattern `{'key': String? val}` checks `map.containsKey('key')`. If `'key'` is omitted from the JSON payload, the pattern fails to match at runtime even though `String?` is nullable. Extract optional keys from the validated map directly (`map['key'] as String?`).
2. **Fast-Fail Fallback**: Ensure unmatched or malformed map structures hit an explicit `_ => throw FormatException(...)` arm rather than silently failing an `if-case` check.

## Examples

### Polymorphic JSON Deserialization (Discriminated Unions)
Use Map patterns with switch expressions to validate tagged JSON payloads and
construct `sealed` class hierarchies. See
[examples/json_patterns.dart](examples/json_patterns.dart) for an executable
implementation demonstrating tagged `ApiResponse` parsing into `SuccessResponse`
and `ErrorResponse`.

### Nested JSON Validation and Optional Fields
Use nested Map and List patterns to validate required schema structure and
extract collections in a single step. See
[examples/json_patterns.dart](examples/json_patterns.dart) for an executable
implementation of `processUserPayload`.

Map patterns check for key existence (`containsKey`). If an optional JSON key
might be omitted entirely from the payload (rather than explicitly passed as
`'key': null`), destructure required keys via the pattern and extract optional
fields directly from the matched submap.

### List and Path Segment Destructuring
Use `if-case` with list rest elements (`...final rest`) to validate prefixes and
extract remaining elements without a 2-arm `_ => null` switch or manual `.first`
and `.skip(1)` indexing.

```dart
String? resolveAllowedAssetSubpath(List<String> segments) {
  if (segments case ['assets', ...final rest]
      when rest.isNotEmpty && !rest.contains('..')) {
    return rest.join('/');
  }
  return null;
}
```

### Algebraic Data Types (Sealed Classes)
Use Object patterns with switch expressions to handle family types exhaustively.

```dart
sealed class Shape {}

class Square implements Shape {
  final double length;
  Square(this.length);
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// Switch expression guarantees exhaustiveness due to `sealed` modifier.
double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(:var radius)   => math.pi * radius * radius,
};
```

### Variable Swapping and Destructuring
Use variable assignment patterns to swap values or extract record fields without temporary variables.

```dart
var (a, b) = ('left', 'right');
(b, a) = (a, b); // Swap values

// Destructuring a function return
var (name, age) = getUserInfo();
```

### Guard Clauses and Logical-or
Use `when` to evaluate arbitrary conditions after a pattern matches.

```dart
switch (shape) {
  case Square(length: var s) || Circle(radius: var s) when s > 0:
    print('Valid positive shape with dimension $s');
  case Square() || Circle():
    print('Zero or negative dimension shape');
}
```

© dart-lang · BSD-3-Clause · dart-lang/skills

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой