关于 dart-language-syntax
面向 Dart 开发者和编码 Agent 的技能,指导编写和现代化惯用 Dart 代码,涵盖状态、函数、记录、模式匹配与泛型。
主要功能
- 01选择 var、final 和 const
- 02使用 tear-off 与生成器
- 03通过记录返回并解构数据
- 04使用模式匹配和 switch 表达式
- 05编写类型安全的泛型代码
适用场景
- 01现代化旧版 Dart 代码
- 02用记录替代元组类
- 03使用模式匹配验证 JSON
- 04用 switch 表达式重构条件逻辑
安装
复制下面的命令,在终端中运行。
$
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修饰符,将非空变量的初始化延迟到首次使用时,尤其适用于开销较大的计算,或需要访问this才能初始化的情况。 - 使用通配符: 使用通配符变量
_(需要 Dart 3.7+),在局部声明、闭包或模式匹配中丢弃未使用的值,且不会触发未使用变量警告。
函数和闭包
让函数结构具备最大的可组合性,并尽可能减少样板代码。
- 使用箭头语法: 使用
=>运算符简化单表达式函数。 - 优先使用方法 tear-off: 直接传递函数引用(tear-off),而不是用多余的匿名闭包包装它们(例如,使用
list.forEach(print),而不是list.forEach((e) => print(e)))。 - 实现生成器: 使用
sync*延迟生成Iterable序列,使用async*生成Stream序列。使用yield生成值,或使用yield*委托给其他生成器。 - 定义命名参数: 对于带有布尔标志或多个参数的函数,使用命名参数
({required Type name}),以提高调用处的可读性。
记录和模式匹配
使用记录和模式消除样板数据类及复杂条件逻辑。
- 返回多个值: 使用记录
(Type, Type)从函数返回多个值,而无需定义专用类。 - 解构赋值: 掌握模式匹配,直接将记录、列表和对象解构到局部变量中。
- 使用 switch 表达式: 使用 switch 表达式替代复杂的
if-else链。借助逻辑或模式(||)和守卫子句(when),让多个分支共享逻辑。 - 以声明式方式验证 JSON: 使用映射和列表模式,同时验证结构、检查类型,并从动态 JSON 负载中提取数据。
泛型和类型安全
确保集合和自定义组件具备类型安全性和可复用性。
- 实现泛型: 使用
<T>为类、方法和集合设置参数化类型。 - 限制类型参数: 使用
extends限定泛型类型(例如,使用<T extends Object>强制非空,或使用<T extends BaseWidget>)。 - 使用 F-Bounds: 当类必须与其自身确切类型的实例交互时,实施自引用类型约束(例如,
class Node<T extends Node<T>>)。 - 扩展功能: 使用扩展方法和扩展类型,为现有的泛型类或具体类添加实用函数,而无需创建子类。
工作流:重构为符合 Dart 惯用风格的代码
使用此清单,将旧版 Dart 代码升级为符合现代 Dart 3+ 标准的惯用代码。
- 任务进度:变量现代化
- 对于一目了然的局部赋值,使用
var替代显式类型。 - 如果可变的
var声明从未被重新赋值,则将其改为final。 - 将未使用的参数
__或___替换为标准的_通配符。
- 对于一目了然的局部赋值,使用
- 任务进度:控制流与返回
- 使用原生 Record
(T1, T2)替代自定义的“Tuple”或“Pair”类。 - 将冗余闭包重构为函数/方法 tear-off。
- 将只包含一条语句的函数体改为箭头
=>语法。
- 使用原生 Record
- 任务进度:模式匹配集成
- 如果要从对象中提取多个字段,请使用对象解构:
var User(:name, :age) = user;。 - 如果要验证嵌套 JSON,请使用映射/列表模式替代
is检查和手动类型转换。 - 如果要对代数数据类型(密封类)进行分支处理,请将
switch语句转换为穷尽式switch表达式。
- 如果要从对象中提取多个字段,请使用对象解构:
- 运行验证器 -> 检查错误 -> 修复: 运行
dart analyze和dart format。解决与类型提升或穷尽性相关的任何 lint 错误。
示例
解构多个返回值(Record)
输入(旧版):
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