Skip to content

typeof类型守卫

假设我们有一个名为 padLeft 的函数:

typescript
function padLeft(padding: number | string, input: string): string { 
  throw new Error("尚未实现!"); 
}

我们来扩充一下功能:如果 padding 是 number ,它会将其视为我们想要添加到 input 的空格数;如果 padding 是 string ,它只在 input 上做 padding 。让我们尝试实现:

typescript
function padLeft(padding: number | string, input: string) { 
  return new Array(padding + 1).join(" ") + input; 
}

呃-哦,在 padding + 1 处我们遇到错误。TypeScript 警告我们,运算符 + 不能应用于类型 string | number 和 number ,这是对的。换句话说,我们没有明确检查 padding 是否为 number ,也没有处理它是 string 的情况,所以我们这样做:

typescript
function padLeft(padding: number | string, input: string) { 
  if (typeof padding === "number") { 
    return new Array(padding + 1).join(" ") + input; 
  }
  return padding + input; 
}

如果这大部分看起来像无趣的JavaScript代码,这也算是重点吧。除了我们设置的注解之外,这段 TypeScript 代码看起来就像 JavaScript。我们的想法是,TypeScript的类型系统旨在使编写典型的 JavaScript 代码变得尽可能容易,而不需要弯腰去获得类型安全。

虽然看起来不多,但实际上有很多东西在这里。就像TypeScript使用静态类型分析运行时的值一样,它在JavaScript的运行时控制流构造上叠加了类型分析,如if/else、条件三元组、循环、真实性检查等,这些都会影响到这些类型。

在我们的if检查中,TypeScript看到 typeof padding ==="number" ,并将其理解为一种特殊形式的代码,称为类型保护。TypeScript遵循我们的程序可能采取的执行路径,以分析一个值在特定位置的最具体的可能类型。它查看这些特殊的检查(称为类型防护)和赋值,将类型细化为比声明的更具体的类型的过程被称为缩小。在许多编辑器中,我们可以观察这些类型的变化,我们甚至会在我们的例子中这样做。

TypeScript 可以理解几种不同的缩小结构。

正如我们所见,JavaScript 支持一个 typeof 运算符,它可以提供有关我们在运行时拥有的值类型的非常基本的信息。TypeScript 期望它返回一组特定的字符串:

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

就像我们在 padLeft 中看到的那样,这个运算符经常出现在许多 JavaScript 库中,TypeScript 可以理解为,它缩小在不同分支中的类型。

在 TypeScript 中,检查 typeof 的返回值是一种类型保护。因为 TypeScript 对 typeof 操作进行编码,从而返回不同的值,所以它知道对 JavaScript 做了什么。例如,请注意在上面的列表中, typeof 不返回 string null 。查看以下示例:

typescript
function printAll(strs: string | string[] | null) { 
  if (typeof strs === "object") { 
    for (const s of strs) { 
      console.log(s); 
    } 
  } else if (typeof strs === "string") { 
    console.log(strs); 
  } else { 
    // 做点事 
  } 
}

在 printAll 函数中,我们尝试检查 strs 是否为对象,来代替检查它是否为数组类型(现在可能是强调数组是 JavaScript 中的对象类型的好时机)。但事实证明,在 JavaScript 中, typeof null 实际上也是 "object" ! 这是历史上的不幸事故之一。

有足够经验的用户可能不会感到惊讶,但并不是每个人都在 JavaScript 中遇到过这种情况;幸运的是, typescript 让我们知道, strs 只缩小到 string[] | null ,而不仅仅是 string[] 。

这可能是我们所谓的“真实性”检查的一个很好的过渡。