# Narrowing 缩小(类型细化)

将类型细化为比声明更具体的类型的过程称为缩小

# 通过typeof判断类型

通过typeof判断类型可以得出下列基本类型

  • "string"
  • "number"
  • "bigint"
  • "boolean"
  • "symbol"
  • "undefined"
  • "object"
  • "function"

typeof null === 'object',所以对null需要更细化的处理

# Truthiness narrowing

if条件语句中,0、NaN、"" (the empty string)、 0n (the bigint version of zero)、 null、 undefined这些值都是false,其他都是true

多个条件处理null


function printAll(strs: string | string[] | null) {
  if (strs !== null) {
    if (typeof strs === "object") {
      for (const s of strs) {
        console.log(s);
      }
    } else if (typeof strs === "string") {
      console.log(strs);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

实例二


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;
  }
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# in 操作符缩写范围

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    return animal.swim();
  }

  return animal.fly();
}

type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = {  swim?: () => void, fly?: () => void };

function move(animal: Fish | Bird | Human) {
  if ("swim" in animal) { 
//   (parameter) animal: Fish | Human
  } else {
// (parameter) animal: Bird | Human
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# instanceof 操作符缩写范围


function logValue(x: Date | string) {
  if (x instanceof Date) {
    console.log(x.toUTCString());
               
    // (parameter) x: Date
  } else {
    console.log(x.toUpperCase());
               
    // (parameter) x: string
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 自动分配类型

let x = Math.random() < 0.5 ? 10 : "hello world!";
   
// let x: string | number

x = true; // Error
1
2
3
4
5

# 控制流分析

# 类型断言

要定义用户定义的类型防护,我们只需要定义一个返回类型为类型谓词的函数:

谓词的形式为parameterName is Type,其中parameterName必须是当前函数签名中参数的名称。

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

1
2
3
4

有可能使用 isFish来过滤类型是 Fish | Bird的数组,并过滤出是Fish的数组

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);
});
1
2
3
4
5
6
7
8
9
10

# 联合类型


interface Shape {
  kind: "circle" | "square";
  radius?: number;
  sideLength?: number;
}

function handleShape(shape: Shape) {
  // oops!
  if (shape.kind === "rect") {
//自动检测出错误 This condition will always return 'false' since the types '"circle" | "square"' and '"rect"' have no overlap.
    // ...
  }
}


function getArea(shape: Shape) {
  if (shape.kind === "circle") {
    return Math.PI * shape.radius! ** 2;
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

更好的实践

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

function getArea(shape: Shape) {
  if (shape.kind === "circle") {
    return Math.PI * shape.radius ** 2;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}


interface Triangle {
  kind: "triangle";
  sideLength: number;
}

type Shape = Circle | Square | Triangle;

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;
      // Type 'Triangle' is not assignable to type 'never'.
      return _exhaustiveCheck;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30