Procházet zdrojové kódy

[non-]nullable types. closed #151

zhongsp před 7 roky
rodič
revize
538be671a2
1 změnil soubory, kde provedl 105 přidání a 2 odebrání
  1. 105 2
      doc/handbook/Advanced Types.md

+ 105 - 2
doc/handbook/Advanced Types.md

@@ -39,7 +39,7 @@ var n = jim.name;
 jim.log();
 ```
 
-# 联合类型
+# 联合类型(Union Types)
 
 联合类型与交叉类型很有关联,但是使用上却完全不同。
 偶尔你会遇到这种情况,一个代码库希望传入`number`或`string`类型的参数。
@@ -123,7 +123,7 @@ pet.swim();    // errors
 我们不能确定一个`Bird | Fish`类型的变量是否有`fly`方法。
 如果变量在运行时是`Fish`类型,那么调用`pet.fly()`就出错了。
 
-# 类型保护与区分类型
+# 类型保护与区分类型(Type Guards and Differentiating Types)
 
 联合类型非常适合这样的情形,可接收的值有不同的类型。
 当我们想明确地知道是否拿到`Fish`时会怎么做?
@@ -283,6 +283,109 @@ if (padder instanceof StringPadder) {
 
 以此顺序。
 
+# 可以为null的类型
+
+TypeScript具有两种特殊的类型,`null`和`undefined`,它们分别具有值null和undefined.
+我们在[基础类型](./Basic Types.md)一节里已经做过简要说明。
+默认情况下,类型检查器认为`null`与`undefined`可以赋值给任何类型。
+`null`与`undefined`是所有其它类型的一个有效值。
+这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行。
+`null`的发明者,Tony Hoare,称它为[价值亿万美金的错误](https://en.wikipedia.org/wiki/Null_pointer#History)。
+
+`--strictNullChecks`标记可以解决此错误:当你声明一个变量时,它不会自动地包含`null`或`undefined`。
+你可以使用联合类型明确的包含它们:
+
+```ts
+let s = "foo";
+s = null; // 错误, 'null'不能赋值给'string'
+let sn: string | null = "bar";
+sn = null; // 可以
+
+sn = undefined; // error, 'undefined'不能赋值给'string | null'
+```
+
+注意,按照JavaScript的语义,TypeScript会把`null`和`undefined`区别对待。
+
+`string | null`,`string | undefined`和`string | undefined | null`是不同的类型。
+
+## 可选参数和可选属性
+
+使用了`--strictNullChecks`,可选参数会被自动地加上`| undefined`:
+
+```ts
+function f(x: number, y?: number) {
+    return x + (y || 0);
+}
+f(1, 2);
+f(1);
+f(1, undefined);
+f(1, null); // error, 'null' is not assignable to 'number | undefined'
+```
+
+可选属性也会有同样的处理:
+
+```ts
+class C {
+    a: number;
+    b?: number;
+}
+let c = new C();
+c.a = 12;
+c.a = undefined; // error, 'undefined' is not assignable to 'number'
+c.b = 13;
+c.b = undefined; // ok
+c.b = null; // error, 'null' is not assignable to 'number | undefined'
+```
+
+## 类型保护和类型断言
+
+由于可以为null的类型是通过联合类型实现,那么你需要使用类型保护来去除`null`。
+幸运地是这与在JavaScript里写的代码一致:
+
+```ts
+function f(sn: string | null): string {
+    if (sn == null) {
+        return "default";
+    }
+    else {
+        return sn;
+    }
+}
+```
+
+这里很明显地去除了`null`,你也可以使用短路运算符:
+
+```ts
+function f(sn: string | null): string {
+    return sn || "default";
+}
+```
+
+如果编译器不能够去除`null`或`undefined`,你可以使用类型断言手动去除。
+语法是添加`!`后缀:`identifier!`从`identifier`的类型里去除了`null`和`undefined`:
+
+```ts
+function broken(name: string | null): string {
+  function postfix(epithet: string) {
+    return name.charAt(0) + '.  the ' + epithet; // error, 'name' is possibly null
+  }
+  name = name || "Bob";
+  return postfix("great");
+}
+
+function fixed(name: string | null): string {
+  function postfix(epithet: string) {
+    return name!.charAt(0) + '.  the ' + epithet; // ok
+  }
+  name = name || "Bob";
+  return postfix("great");
+}
+```
+
+本例使用了嵌套函数,因为编译器无法去除嵌套函数的null(除非是立即调用的函数表达式)。
+因为它无法跟踪所有对嵌套函数的调用,尤其是你将内层函数做为外层函数的返回值。
+如果无法知道函数在哪里被调用,就无法知道调用时`name`的类型。
+
 # 类型别名
 
 类型别名会给一个类型起个新名字。