#TypeScript文档学习笔记2 ##Narrowing
function padLeft(padding: number | string, input: string) { if (typeof padding === "number") { return new Array(padding + 1).join(" ") + input; } return padding + input; }
除了加入了类型注释之外,这段代码看起来就像一段平平无奇的 JavaScript 代码,但是 TypeScript 的类型分析覆盖在了 JavaScript 的控制流上,例如 if/else
,for loop
, A ? B : C
等等。
在上面的 if
中,TypeScript 看到 typeof padding === "number"
则认为这是一种类型保护
。这种 类型保护,将类型范围缩小到到一个具体类型上,这个细化的过程叫做 {Narrowing} 。
###typeof 类型检查
使用typeof
命令会返回的基本值
- string
- number
- bigint
- boolean
- symbol
- undefined
- object
- function
需要注意的是,
typeof null === ’object‘
,所以对 null 出现情况要进行真实性判断。
###真实性判断 在 if 判断里,一般会把条件收束在一个布尔值上,然后根据 true or false 来选择分支。在 JavaScript 中,以下的值都会被认为是 false :
- 0
- NaN
""
(the empty string)- 0n (the bigint version of zero)
- null
- undefined 其他所有值都被认为是true。一般也可以用 Boolean 函数或者双重布尔否定将值收束到布尔值。 区别如下: // both of these result in 'true' Boolean("hello"); // type: boolean, value: true !!"world"; // type: true, value: true
使用 ! 来进行真实性判断: function multiplyAll( values: number[] | undefined, factor: number ): number[] | undefined { if (!values) { return values; } else { return values.map((x) => x * factor); } }
TypeScript 也用 ===
,!==
,==
,!=
来进行类型检查。
//example
function printAll(strs: string | string[] | null) {
if (strs !== null) {
if (typeof strs === "object") {
for (const s of strs) {
(parameter) strs: string[] console.log(s); } } else if (typeof strs === "string") { console.log(strs);
(parameter) strs: string } } }
在 JavaScript 中,==
是个松散的相等, a == null
在程序判断的时候不仅在检查是否是 null
, 也检查了 a 是否是 undefined
。
interface Container {
value: number | null | undefined;
}
function multiplyValue(container: Container, factor: number) { // Remove both 'null' and 'undefined' from the type. if (container.value != null) { console.log(container.value);
// Now we can safely multiply 'container.value'.
container.value *= factor;
} }
in
运算符进行narrowing
type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = { swim?: () => void, fly?: () => void };
function move(animal: Fish | Bird | Human) { if ("swim" in animal) { animal //(parameter) animal: Fish | Human } else { animal //(parameter) animal: Bird | Human } }
instanceof
narrowing
x instanceof X
会检查 x 的原型链上是否包含 X.prototype
。
function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString());
(parameter) x: Date } else { console.log(x.toUpperCase());
(parameter) x: string } }
is
的使用
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()]; const underWater1: Fish[] = zoo.filter(isFish); // or, equivalently const underWater2: Fish[] = zoo.filter(isFish) as Fish[];
// The predicate may need repeating for more complex examples const underWater3: Fish[] = zoo.filter((pet): pet is Fish => { if (pet.name === "sharkey") return false; return isFish(pet); });
Discriminated Union
对于有一些些许不同的形状,我们可能会这么去定义。 circle 有 radius, square 有 sideLength。 interface Shape { kind: "circle" | "square"; radius?: number; sideLength?: number; }
但其实这样设计会有很多问题,虽然对kind
进行了类型检查,但是 TypeScript 在strictNullChecks
模式下,依然不知道 shape.radius
是否存在,我们需要加上 ! 操作符来对 shape.radius
进行合法判断。
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
//Object is possibly 'undefined'.
}
}
//correct function getArea(shape: Shape) { if (shape.kind === "circle") { return Math.PI * shape.radius! ** 2; } }
这样其实并不理想,且容易出错。究其原因,是类型检查无法根据 kind
推测出 radius
sideLendth
是否存在。由此,我们重新设计 Shape
:
interface Circle {
kind: "circle";
radius: number;
}
interface Square { kind: "square"; sideLength: number; }
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
//(parameter) shape: Circle
case "square":
return shape.sideLength ** 2;
//(parameter) shape: Square
}
}
这样根据对 kind
的 narrowing ,类型检查就能根据 kind
推测出 radius
sideLendth
是否存在。在 JavaScript 中,消息传递的模型可以这样设计。
###Nerver Type
never
可以分配给任何 type ,但是没有任何类型可以分配给 never
。
//example
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
但如果向 Shape 中添加一个 Triangle,则需要在 kind 中检查是否为 triangle,否则_exhaustiveCheck
此时会报错 Type 'Triangle' is not assignable to type 'never'.