TypeScript文档学习笔记2

199 阅读3分钟

#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 loopA ? 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'.