迁移到 Dart null 安全:迁移三元运算符 null 检查的最佳实践?单子方法是否太不传统了?
我正在将代码库迁移到 null 安全,其中包含大量如下代码:
MyType convert(OtherType value) {
return MyType(
field1: value.field1,
field2: value.field2 != null ? MyWrapper(value.field2) : null,
);
}
不幸的是,三元运算符不支持通过 null 检查进行类型提升,这意味着我必须将 !
添加到断言它不为空,以便使其在空安全下编译:
MyType convert(OtherType value) {
return MyType(
field1: value.field1,
field2: value.field2 != null ? MyWrapper(value.field2!) : null,
);
}
这使得代码有点不安全;人们可以很容易地想象这样一种场景:空检查被修改,或者某些代码被复制并粘贴到 !
导致崩溃的情况中。
所以我的问题是是否有特定的最佳实践来更安全地处理这种情况?重写代码以直接利用流分析和类型提升是很麻烦的:
MyType convert(OtherType value) {
final rawField2 = value.field2;
final MyWrapper? field2;
if (rawField2 != null) {
field2 = MyWrapper(rawField2);
} else {
field2 = null;
}
return MyType(
field1: value.field1,
field2: field2,
);
}
作为一个在函数式编程方面思考很多的人,我的本能是将可空类型视为一个 monad,并定义 map
相应地:
extension NullMap<T> on T? {
U? map<U>(U Function(T) operation) {
final value = this;
if (value == null) {
return null;
} else {
return operation(value);
}
}
}
那么这种情况可以这样处理:
MyType convert(OtherType value) {
return MyType(
field1: value.field1,
field2: value.field2.map((f) => MyWrapper(f)),
);
}
这似乎是保持安全性和简洁性的好方法。然而,我在网上进行了长时间的搜索,但找不到其他人在 Dart 中使用这种方法。有一些定义 Optional
monad 的包示例似乎早于 null 安全性,但我找不到任何 Dart 开发人员直接在可空类型上定义 map
的示例。这里有我遗漏的主要“陷阱”吗?在 Dart 中是否还有另一种既符合人体工程学又更传统的方法?
I'm migrating a code base to null safety, and it includes lots of code like this:
MyType convert(OtherType value) {
return MyType(
field1: value.field1,
field2: value.field2 != null ? MyWrapper(value.field2) : null,
);
}
Unfortunately, the ternary operator doesn't support type promotion with null checks, which means I have to add !
to assert that it's not null in order to make it compile under null safety:
MyType convert(OtherType value) {
return MyType(
field1: value.field1,
field2: value.field2 != null ? MyWrapper(value.field2!) : null,
);
}
This makes the code a bit unsafe; one could easily image a scenario where the null check is modified or some code is copied and pasted into a situation where that !
causes a crash.
So my question is whether there is a specific best practice to handle this situation more safely? Rewriting the code to take advantage of flow analysis and type promotion directly is unwieldy:
MyType convert(OtherType value) {
final rawField2 = value.field2;
final MyWrapper? field2;
if (rawField2 != null) {
field2 = MyWrapper(rawField2);
} else {
field2 = null;
}
return MyType(
field1: value.field1,
field2: field2,
);
}
As someone who thinks a lot in terms of functional programming, my instinct is to think about about nullable types as a monad, and define map
accordingly:
extension NullMap<T> on T? {
U? map<U>(U Function(T) operation) {
final value = this;
if (value == null) {
return null;
} else {
return operation(value);
}
}
}
Then this situation could be handled like this:
MyType convert(OtherType value) {
return MyType(
field1: value.field1,
field2: value.field2.map((f) => MyWrapper(f)),
);
}
This seems like a good approach to maintain both safety and concision. However, I've searched long and hard online and I can't find anyone else using this approach in Dart. There are a few examples of packages that define an Optional
monad that seem to predate null safety, but I can't find any examples of Dart developers defining map
directly on nullable types. Is there a major "gotcha" here that I'm missing? Is there another approach this is both ergonomic and more conventional in Dart?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这个前提是不正确的。三元运算符确实进行类型提升。但是,只有本地变量或既是
final
又是私有的实例变量可以进行类型提升。另请参阅:因此,您应该只引入一个局部变量(您似乎已经在
if
-else
和NullFlatMap
示例中实现了这一点):This premise is not correct. The ternary operator does do type promotion. However, only local variables or instance variables that are both
final
and private can be type-promoted. Also see:Therefore you should just introduce a local variable (which you seem to have already realized in your
if
-else
andNullFlatMap
examples):