
这次 Dart 3.15 有个改动虽然不大但我感觉算是解决了 Dart 类型系统里一个存在多年的不合理限制编译器明明已经知道当前对象是什么类型但是就是不给开发者直接按照这个类型用它。从 3.15 开始this终于可以像局部变量一样根据is判断、空值检查之类条件也可以自动改变自己的静态类型class Shape { void describe() { var self this; if (self is Circle) { print(radius ${self.radius}); } } } class Circle extends Shape { final double radius; Circle(this.radius); }比如这里Shape是父类Circle是子类子类独有一个radius属性如果执行Circle(10).describe();虽然describe()定义在Shape里面但是实际执行它的对象是Circle所以进入方法的时候this的声明静态类型是Shapethis在运行时指向的对象是Circle经过is Circle判断之后编译器知道当前对象确实是Circle实际上以前的问题刚好就是出在第三点以前的 Dart 就算知道判断结果也不会修改this的静态类型所以如果直接写会变异错误class Shape { void describe() { if (this is Circle) { print(this.radius); // 旧版本编译错误 } } }编译器还是按照Shape查找radius自然就会报错因为Shape根本没有这个字段。而为了绕过限制开发者必须先写var self this然后让self参与类型提升这是因为 Dart 以前只支持特定变量的类型提升不支持直接提升this。而到了 Dart 3.15 现在这个代码终于可以正常工作了在if成立的分支里this的静态类型可以由Shape提升为Circle。class Shape { void describe() { if (this is Circle) { print(radius $radius); } } }而且不只是this.radius可以访问直接写radius也可以因为类实例方法里radius这样的隐式实例成员访问编译器也会考虑已经提升的this类型也就是父类方法可以直接访问经过类型判断确认的子类成员不用反复强制转换。其实就是变化的是编译器在对应代码分支中理解的静态类型不是对象本身的实际类型。另一个更实用的场景是 Nullable ExtensionDart 允许给可空类型添加 Extension例如int?extension NullableInt on int? { bool get isEvenOrNull { final self this; if (self null) { return true; } return self.isEven; } }这里this的类型是int?也就是既可能是整数也可能是null按照常规的空安全规则检查self null并提前返回以后后面的self就会从int?提升成int可以直接使用isEven。但是以前直接检查this编译器不会完成这种提升必须额外定义一个self。而现在 Dart 3.15 可以直接写extension NullableInt on int? { bool get isEvenOrNull { if (this null) return true; return isEven; } } void main() { int? a 4; int? b 3; int? c; print(a.isEvenOrNull); // true print(b.isEvenOrNull); // false print(c.isEvenOrNull); // true }这里有两个变化首先if (this null) return true之后编译器知道剩余路径的this必然是非空int其次return isEven里面隐式使用的this也会享受到这次提升最重要的是Extension 的接收对象完整参与 Dart 的空安全控制流分析 以前局部变量能做到的事情现在 Extension 里的this也能做了。类似专业的场景这里的debugInfo写在父类PageState里面但是能够通过this is Loaded和this is Failed直接访问子类的items、message属性sealed class PageState { const PageState(); String get debugInfo { if (this is Loaded) { return 加载成功: ${items.length} 条数据; } if (this is Failed) { return 加载失败: $message; } return 加载中; } } class Loading extends PageState { const Loading(); } class Loaded extends PageState { final ListString items; const Loaded(this.items); } class Failed extends PageState { final String message; const Failed(this.message); }这种机制对 Flutter 中的状态基类、事件体系、插件扩展、基于继承的组件封装都挺有帮助的尤其是存在大量类型分支判断的代码。当然上面的例子只是为了演示类型提升实际业务如果已经用sealed class、switch模式匹配或者可以通过多态完成状态处理一般其实不需要为了用这个新特性而改回一大堆if (this is Xxx)。至于为什么 Dart 到 3.15 才支持这里其实涉及 Dart 类型系统长期以来对类型提升的谨慎因为 Dart 的类型提升不是看到一个判断就无条件修改类型实际上编译器还需要确认被检查的值在后续代码中是否足够稳定比如class Test { int? value; void run() { if (value ! null) { // value 仍然可能变化 } } }value是一个可变成员编译器不能因为检查过一次就永远假定它非空对 Getter 来说连续两次调用甚至可能返回不同结果。而this本质上有个明显优势一个实例方法执行期间当前接收对象不会突然变成另一个对象所以只要条件能够证明当前对象满足某个类型编译器就可以在相应控制流中安全地使用这个信息。这里和 Dart 3.2 的字段类型提升还是有区别的对比Dart 3.2Dart 3.15局部变量类型提升支持支持私有final字段提升支持支持this is SubType后提升this不支持支持Nullable Extension 中提升this不支持支持隐式this成员访问使用提升结果不支持支持等于是Dart 3.2 解决的是一部分稳定字段的类型提升问题3.15 进一步补上了当前对象本身的类型提升。