跳至主要內容

类型推论

樱桃茶大约 6 分钟

类型推论

类型推论(Type Inference)是 TypeScript 的一个特性,它帮助你在不显式指定类型的情况下为变量和表达式自动确定类型。当 TypeScript 编译器处理代码时,它尽可能地根据现有代码为未标注类型的部分推断出类型信息。

以下是关于类型推论的一些关键点和例子:

  • 基础类型推断:

    • 当你给变量赋值时,TypeScript 会推断出一个类型。
      • 示例:let x = 3; 这里 TypeScript 推断 xnumber 类型。
    • 如果没有明确的赋值,通常会默认为 any 类型,表示可以是任何类型。
      • 示例:let y; 这里 y 的类型被推断为 any
  • 最佳通用类型:

    • 当涉及到多个类型的推理时,例如数组中包含不同类型的元素,TypeScript 会尝试找到一个兼容所有元素的最佳通用类型。
      • 示例:let z = [0, '1', false]; 这里 z 被推断为 (string | number | boolean)[] 类型。
  • 上下文类型:

    • 在某些情况下,TypeScript 会根据左侧的值或位置来确定右侧表达式的类型。
      • 示例:window.onmousedown = function(mouseEvent) { console.log(mouseEvent.button); }; 这里 mouseEvent 参数的类型被推断为 MouseEvent,这是因为 onmousedown 的类型定义了事件处理函数的参数应该是 MouseEvent 类型。

类型推论使得编写 TypeScript 更加简洁,因为你不需要显式为变量或者返回值指定类型,编译器会自动为你做这件事。但是有时候过度依赖类型推断会导致代码可读性降低,尤其是当推断出的类型不是你预期的类型时。因此,建议至少为函数参数和返回类型提供明确的类型注解,以提高代码的清晰度和维护性。

基础

类型推论是 TypeScript 中的一项特性,它允许 TypeScript 编译器自动地为变量推断出一个类型。在你不显式声明变量类型的情况下,TypeScript 会根据变量的值来推断其类型。

以下是类型推论的基础内容:

  • 当你初始化变量时,TypeScript 会根据赋予的值推断出变量的类型。

    • 例如:
      let x = 3; // 类型被推断为number
      
  • 如果你没有立即初始化变量,而是稍后再赋值,那么它将被推断为any类型。

    • 例如:
      let y; // 类型被推断为any
      y = "Hello"; // y现在是一个字符串
      y = 10; // y现在是一个数字
      
  • 在函数中,TypeScript 也会对返回值进行类型推断。

    • 例如:
      function add(a, b) {
        return a + b;
      }
      // 函数返回值类型被推断为number
      
  • 但是,最佳实践是尽可能显式声明变量、函数参数和返回值的类型,以避免意外的类型推断。

    • 例如:
      function add(a: number, b: number): number {
        return a + b;
      }
      // 这样函数的参数和返回值类型都明确指定为了number
      

理解这些基础可以帮助你更好地把握 TypeScript 的类型系统,减少因为错误的类型推断导致的潜在 bug,并且有助于你编写更加清晰和可维护的代码。

最佳通用类型

  • 在 TypeScript 中,类型推论(Type Inference)是指编译器自动确定表达式类型的能力。这意味着即使你不显式地指定变量的类型,TypeScript 也能根据你的代码自动为其分配最合适的类型。

  • 最佳通用类型(Best common type)是类型推论中的一个概念。当需要从多个表达式推断出一个单一类型时,TypeScript 会尝试找到这些表达式共同兼容的最具体的类型。

  • 例如,当你有一个数组或者混合值时,TypeScript 会评估这些值并找到它们所有共有的“最佳”类型。

let arr = [0, 1, null]; // 数组包含了 number 和 null 类型
  • 在这个例子中,arr的元素类型被推断为number | null(即数字或 null 的联合类型),因为这是数组中所有元素可以共同拥有的最具体的类型。

  • 若无一个类型能作为所有候选类型的超类型(即共同父类型),那么类型推论将使用这些类型的联合类型。

let arr2 = [1, "hello", true]; // 数组包含了 number, string, 和 boolean 类型
  • 这里,arr2的类型被推断为(number | string | boolean)[],即数组的每个元素都可以是numberstringboolean

  • 理解"最佳通用类型"有助于编写更灵活且类型安全的代码,尤其是在处理复杂数据结构时。但也要注意,过度依赖类型推论可能会降低代码的可读性和维护性,特别是在复杂的项目中。因此,在实践中,明确类型声明与类型推论应该根据情况权衡使用。

上下文类型

上下文类型是 TypeScript 的类型推论的一个方面,它是指编译器能够根据代码的上下文来推断表达式的类型。当你在特定的位置写代码时,TypeScript 会使用这些位置的信息来帮助确定应该使用哪种类型。下面是一些关于上下文类型的关键点及其示例:

  • 上下文归类:当函数的参数存在类型时,TypeScript 会利用这个信息来推断传递给函数的表达式的类型。

    • 示例:

      function onCreate(buttonClick: (event: MouseEvent) => void) {
        // ...
      }
      
      onCreate((event) => {
        // TypeScript 推断 event 是一个 MouseEvent 类型
        console.log(event.button); // 正确
      });
      
  • 在事件处理中:通常在添加事件监听器时,TypeScript 可以根据被监听的事件类型自动推断事件参数的类型。

    • 示例:
      document.addEventListener("click", (event) => {
        // TypeScript 推断 event 是一个 MouseEvent 类型
        console.log(event.clientX, event.clientY);
      });
      
  • 对象字面量属性推断:对象字面量会被赋予一个上下文类型,如果这个对象字面量的上下文明确了某个属性的类型,TypeScript 将会推断对应属性值的类型。

    • 示例:

      type Point = {
        x: number;
        y: number;
      };
      
      function drawCoord(point: Point) {
        // ...
      }
      
      drawCoord({ x: 100, y: 200 });
      // TypeScript 推断 point 参数需要的是一个 Point 类型的对象,
      // 因此对象字面量 { x: 100, y: 200 } 中的 x 和 y 被推断为数字。
      
  • 上下文类型会影响函数表达式和箭头函数的参数类型,但不会影响已命名函数的参数类型。

    • 示例:

      const names = ["Alice", "Bob", "Charlie"];
      names.forEach(function (s) {
        // 这里 s 的类型不能被推断为 string,因为已命名函数不使用上下文类型
      });
      
      names.forEach((s) => {
        // 这里 TypeScript 推断 s 是 string 类型,因为箭头函数使用了上下文类型
      });
      

理解上下文类型有助于你编写更少的类型标注,同时让代码保持清晰和正确性。随着你继续使用 TypeScript,你将更多地看到和利用上下文类型以便简化代码并减少不必要的类型声明。