> For the complete documentation index, see [llms.txt](https://docs.hong97.ltd/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hong97.ltd/typescript/narrowing.md).

# Narrowing

凭借 TypeScript ，它可以对许多类型进行自动 narrowing，以此来更好的辅助 coding

## typeof

`typeof` 返回以下 8 种类型。

* `"string"`
* `"number"`
* `"bigint"`
* `"boolean"`
* `"symbol"`
* `"undefined"`
* `"object"`
* `"function"`

## Truthiness

> 在 JavaScript 中，Array, null 都是 object 类型

```js
typeof null
> 'object'
```

因此，如果要通过 `typeof` 来判断一个变量是否为 array 可能会带来意想不到的问题，考虑以下代码：

```typescript
function foo(str: string[] | null) {
  if(typeof str === 'object'){
    // 报错，因为 s 可能是 array，但也可能是 null！
    for(const s of str){
      ...
    }
  }
}
```

如果使用 JavaScript 编写，上述问题就不能被正确发现，好在 TypeScript 会帮你指出这一问题，因此正确的解决方法是:

```typescript
function foo(str: string[] | null) {
  if(str && typeof str === 'object'){
    // ok, 先进行了 truthiness 判断
    for(const s of str){
      ...
    }
  }
}
```

## ==, !=

Typescript 在判断 `x != undefined` 的时候，会同时判断 `x != null`，反之依然（`x == undefined`)；同时，在判断 `x != null` 的时候，也会同时判断 `x != undefined` ，反之亦然。

```typescript
function foo(x: number | null | undefined) {
  if (x != null) {
    // x != null 实际上过滤掉了 null 和 undefined 两种情况
    // 因此 x 一定是 number
    console.log(x.toFixed(2))
  }
}
```

## 自定义类型保护

我们可以自己封装一个判断函数，来判断某一变量是否为指定的类型

```typescript
function isString(test: string | number): test is string {
  return typeof test === 'string'
}

function foo(bar: any){
  if(isString(bar)){
    console.log("it's a string")
    console.log(bar.toFixed(2))		// 会在编译时报错，因为 bar 是 string
  }
}
```

使用 `<var> is <type>` 谓词作为这个判断函数的返回值，ts 会在 if 逻辑后 narrow 它的类型

![image-20211226181638088](https://github.com/hongding0211/frontend-notes/blob/main/imgs/image-20211226181638088.png)

bar 已经从 `any` narrow 到 `string` 了，因此会直接报错

那么为什么不直接返回一个 `boolean` 呢？

```typescript
function isString(test: string | number): boolean {
  return typeof test === 'string'
}

function foo(bar: any){
  if(isString(bar)){
    console.log("it's a string")
    console.log(bar.toFixed(2))		// 编译时不会报错，而 runtime 时会报错
  }
}
```

TypeScript 的 narrowing 机制需要借助 `is` 来实现，因此直接返回一个 `boolean` 并不可行

![image-20211226181743033](https://github.com/hongding0211/frontend-notes/blob/main/imgs/image-20211226181743033.png)

在 if 逻辑后，ts 并不能正确地 narrow bar 的属性，它仍然是一个 `any`

## Discriminated unions

定义两种 Shape，Circle 和 Square，它们都有一个 `kind` 属性

```typescript
interface Circle {
  kind: "circle";
  radius: number;
}
 
interface Square {
  kind: "square";
  sideLength: number;
}

function foo(shape: Circle| Square){
  if(shape.kind === 'circle'){
    // 这时候 shape 已经被 narrow 到 Circle
    // 因此只能访问 radius 属性
    console.log(shape.radius)
  }
  ...
}
```

那么在 if 逻辑中对 kind 进行判断，相当于进行了 narrow

也可以使用 switch 语句对 kind 进行遍历

## never type

如果 TypeScript 不断地 narrowing，直到没有一个类型都没有剩下，那这时候它就是 `never` 类型

```typescript
function getArea(shape: Circle | Square) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    default:
      // 这时候 shape 就是被 narrow 到 never
      const _exhaustiveCheck: never = shape;		
      return _exhaustiveCheck;
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hong97.ltd/typescript/narrowing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
