👨‍💻
Hong's 前端笔记
  • 🌈About
  • JavaScript
    • 作用域
    • 闭包
    • this
      • bind()
    • 原型
      • Object.create()
      • 模拟“类”
      • ES6 Class
      • Function
    • 对象
    • 类型 & 值
      • 原生函数
      • 数组
      • 字符串
    • 异步
    • JavaScript 相关概念
      • Event Loop 事件循环
      • Prototype 原型
      • Context 执行上下文
      • this
      • Promise
    • JavaScript 常见问题
      • 手写实现
      • 看代码输出
  • React
    • 核心概念
    • 深入理解
      • State
      • 如何管理 State
      • React 渲染过程
      • 处理 DOM 事件
      • Pure 组件
      • Context
      • Key 属性
      • React.lazy()
      • Render Props
    • Hooks
      • useState
      • useRef
      • useEffect
        • Some details
      • 相关概念
      • 自定义 Hook
      • useEffect vs useLayoutEffect
    • React 相关问题
      • Class vs Function
  • React Libraries
    • Redux
      • Quick Start
      • 如何持久化 Redux 数据
      • 异步操作
      • 性能优化
      • Dive into immutability
    • Route v6
      • Quick Start
  • Reactivity
    • Immer
  • Vue
    • 深入理解
      • Composition vs Options
      • Reactivity
  • Network
    • Cookies
    • HTTP
    • HTTPS
    • CORS 跨域资源共享
    • 认证手段
    • 安全相关
    • 网络相关知识
  • Browser
    • DOM 操作
    • Events 事件
    • XHR & Fetch
    • 性能优化
    • HTML 相关概念
    • 浏览器相关概念
  • CSS
    • 盒模型
    • Layout 布局
    • Styles 样式
    • CSS 相关概念
    • CSS 相关技巧
      • 移动端适配
      • Flex
      • 动画
  • TypeScript
    • Quick Peek
    • Types in TypeScript
    • Narrowing
    • Functions in TypeScript
  • Workflow
    • Webpack
      • Webpack 概念
      • 资源管理
      • 管理输出
      • 开发环境配置
      • 生产环境配置
      • 优化代码运行性能
  • Others
    • 小程序与原生 Web 的区别
  • SSO
    • About
    • API Doc
    • 接入指南
Powered by GitBook
On this page
  • typeof
  • Truthiness
  • ==, !=
  • 自定义类型保护
  • Discriminated unions
  • never type
Edit on GitHub
  1. TypeScript

Narrowing

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

typeof

typeof 返回以下 8 种类型。

  • "string"

  • "number"

  • "bigint"

  • "boolean"

  • "symbol"

  • "undefined"

  • "object"

  • "function"

Truthiness

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

typeof null
> 'object'

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

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

如果使用 JavaScript 编写,上述问题就不能被正确发现,好在 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 ,反之亦然。

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

自定义类型保护

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

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 它的类型

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

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

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 并不可行

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

Discriminated unions

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

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 类型

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

Last updated 3 years ago

image-20211226181638088
image-20211226181743033