文档 / 语言手册 / Null,Undefined & Option
Edit

Null,Undefined & Option

ReScript 本身没有 nullundefined。这实在是件 好事,因为它消除了一整类的 bug。再也不会有 undefined is not a functioncannot access someAttribute of undefined 了!

不过,“可能不存在值”这一概念仍然是有用的,并且安全地存在于我们的语言中。

我们将值用 option 类型包装,来表示值的存在与不存在。下面是它在标准库中的定义:

ReScriptJS Output
type option<'a> = None | Some('a)

它的意思是“option 类型的值要么是 None(什么也没有),要么是被 Some 包装的那个实际值”。

注意 option 类型只是一个普通的变体

示例

这是一个普通的值:

ReScriptJS Output
let licenseNumber = 5

为了表示“可能为空”的概念,你可以通过包装把它变成一个 option 类型。为了更好地说明问题,我们给它加上条件表达式:

ReScriptJS Output
let licenseNumber =
  if personHasACar {
    Some(5)
  } else {
    None
  }

稍后,当另一段代码收到这样的值时,它会被强制通过模式匹配来处理两种情况:

ReScriptJS Output
switch licenseNumber {
| None =>
  Js.log("The person doesn't have a car")
| Some(number) =>
  Js.log("The person's license number is " ++ Js.Int.toString(number))
}

通过把普通数字变成 option 类型并强迫你处理 None 的情况,ReScript 有效地消除了误处理或者忘记处理(概念上的)null 值的可能性!纯 ReScript 程序不会出现 null 错误

与 JavaScript 的 undefinednull 互操作

option 类型很常见,所以我们在编译成 JavaScript 时会对其进行特殊处理:

ReScriptJS Output
let x = Some(5)

会简洁地编译为5,而

ReScriptJS Output
let x = None

会编译为 undefined!如果你在 JavaScript 中有一个字符串,你知道它可能是 undefined,那么把它标注为 option<string> 就完事了!同样地,你可以向 JS 端发送一个 Some(5)None,并期望它被正确解释 =)。

警告 1

optionundefined 的翻译并不是完美的,因为在 ReScript 这边, option 值是可组合的:

ReScriptJS Output
let x = Some(Some(Some(5)))

这仍然会编译为 5,但是下面的代码就不太说的清了:

ReScriptJS Output
let x = Some(None)

这个 Caml_option.some 是什么东西?为什么它不能直接编译成 undefined?长话短说,在处理多态 option 类型时(也就是 option<'a>'a为任意类型),如果我们不对值进行一些特殊的标注,许多操作就会变得很棘手。如果这段说明对你没什么意义,别担心,记住下面的规则就行:

  • 永远不要传递一个嵌套的 option 值(比如 Some(Some(Some(5))))到 JS 端

  • 永远不要将一个来自于 JS 的值标注为 option<'a> 类型。总是使用具体的非多态类型

警告 2

不幸的是,很多时候你的 JavaScript 值既可能是 null 也可能是 undefined。在这种情况下,你不能把这样的值当作 option<int> 类型,因为我们的 option 类型在处理 None 时只检查 undefined 而不检查 null

解决方法:更复杂的 undefinednull 互操作

为了解决这个问题,我们通过 Js.Nullable 模块提供了更复杂的 nullundefined 辅助函数。这有点像 option 类型,但又与之不同。

示例

要创建 JS 的 null,使用 Js.Nullable.null 的值。要创建 JS 的 undefined,使用 Js.Nullable.undefined(你也可以使用 None,但这不是这里的重点;Js.Nullable.* 里的辅助函数不会对它起作用)。

例如,如果你收到的是一个可以为 nullundefined 的 JS 字符串,那就这样声明类型:

ReScriptJS Output
@module("MyConstant") external myId: Js.Nullable.t<string> = "myId"

如果要从 ReScript 这边创建一个可空(nullable)字符串(假设是为了互操作目的,会把它传递给 JS 端),可以这么做:

ReScriptJS Output
@module("MyIdValidator") external validate: Js.Nullable.t<string> => bool = "validate"
let personId: Js.Nullable.t<string> = Js.Nullable.return("abc123")

let result = validate(personId)

return 部分将一个字符串“包装”成一个可空字符串,以使类型系统理解并跟踪这样一个事实:当你传递这个值时,它不仅仅是一个字符串,而是一个可以是 nullundefined 的字符串。

option 值互相转换

Js.Nullable.fromOption 将一个 option 转换到 Js.Nullable.t

Js.Nullable.toOption 做相反的转换。