不一样的 TypeScript :深入了解类型推导与其关键字

类型推导,让类型趋于明确

"类型推导"是指编译器根据代码上下文自动确定变量、函数参数、返回值等的类型,而无需显式地为其指定类型注解。也就是说你想从 TS 角度看待自己撰写的代码,第一步一定是掌握类型推导

当涉足 TypeScript 这门强大的静态类型语言时,“类型推导”(Type Inference)将成为你的得力助手。在 TypeScript 中,类型推导不仅是一项核心特性,更是帮助你编写更安全、更高效代码的关键工具。想象一下,在开发过程中能够让编译器智能地了解你的代码,并根据上下文来推断变量、函数参数和返回值的类型。这是 TypeScript 引以为傲的特点之一,也是使得类型推导成为 TypeScript 魔法的原因。

菜鸟的疑惑?!

刚接触 TypeScript 的朋友可能时常遇到问题如,你不能将 XXX 类型赋予 YYY。或者 Property 'x属性' does not exist on type 'YYY' 的报错。

function foo(input: string | number) {
  input.length; // 错误代码
}

这是因为在 TypeScript 中,当一个变量的类型被确定后,TypeScript 将会基于这个类型来做类型检查和类型推断。然而,在某些情况下,TypeScript 可能无法精确地确定变量的类型。

这时你可能需要显式地告诉 TypeScript 变量的实际类型

这就是类型推导,通过使用 typeof 检查,你可以在代码中显式地告诉 TypeScript,例如当变量的类型是 string 时,可以安全地访问 length 属性。这样,TypeScript 就能够根据你的断言,正确地进行类型推断和类型检查,避免出现错误。

declare const strOrNumOrBool: string | number | boolean;

if (typeof strOrNumOrBool === "string") {
  // 一定是字符串!
  strOrNumOrBool.charAt(1);
} else if (typeof strOrNumOrBool === "number") {
  // 一定是数字!
  strOrNumOrBool.toFixed();
} else if (typeof strOrNumOrBool === "boolean") {
  // 一定是布尔值!
  strOrNumOrBool === true;
} else {
  // 要是走到这里就说明有问题!
  const _exhaustiveCheck: never = strOrNumOrBool;
  throw new Error(`Unknown input type: ${_exhaustiveCheck}`);
}

上述类型推导的简单应用可以应付一部分使用场景,但这不能发挥类型推导的全部功能。

场景:您需要判断一个值是否为真/假值

从简单应用的角度出发似乎你需要

if (
  input === false ||
  input === "" ||
  input === 0 ||
  input === null ||
  input === undefined
) {
  //逻辑
}

看起来难以扩展、且不太优雅不是吗?所以直觉告诉您需要将判断逻辑封装起来提取到函数外部进行复用

于是您写出了这一段代码

function isFalsy(input: unknown): boolean {
  return (
    input === false ||
    input === "" ||
    input === 0 ||
    input === null ||
    input === undefined
  );
}

declare const input: unknown;
if (!isFalsy(input)) {
  input; // 此时鼠标指向input 发现静态推导出的类型是 unknown
}

看起来不太妙! 他根本就没起到任何约束作用。原来 TypeScript 中类型推导分析做不到跨函数上下文来进行类型的信息收集

is 关键字

TypeScript 引入了 is 关键字来显式地提供类型信息。你可以通过is 关键字 + 预期类型来帮助类型推导

function isFalsy(input: unknown): input is boolean {
  return !(
    input === false ||
    input === "" ||
    input === 0 ||
    input === null ||
    input === undefined
  );
}

declare const input: unknown;
if (isFalsy(input)) {
  Boolean(input); // 此时鼠标指向input 发现静态推导出的类型是 boolean
}

需要注意的是 is 关键字相关的类型推导将会无比信任你的设置。

例:以下的一段代码 return 的判断逻辑不会改变以进入 if(isString(input))条件分支里 input 的类型。你会得到一个

通过了typeof input === "string",但类型是 number 的分支环境。

function isString(input: unknown): input is number {
  return typeof input === "string";
}

function foo(input: string | number) {
  if (isString(input)) {
    input.replace("linbudu", "linbudu599"); // Property 'replace' does not exist on type 'number'.
  }
}

typeof 关键字

  1. JavaScript 中的 typeof:

    在 JavaScript 中,typeof 返回一个字符串,表示操作数的基本类型。例如:

    javascriptCopy codeconsole.log(typeof "Hello"); // 输出: "string"
    console.log(typeof 42);      // 输出: "number"
    console.log(typeof true);    // 输出: "boolean"
    
  2. TypeScript 中的 typeof:

    在 TypeScript 中,typeof 用于类型检查和类型推断。它可以用于获取变量的静态类型,也可以用于判断变量的实际类型。例如:

    typescriptCopy codeconst str = "Hello";
    const num = 42;
    
    let strType: typeof str; // 类型推断:strType 的类型为 "string"
    let numType: typeof num; // 类型推断:numType 的类型为 "number"
    

    此外,TypeScript 的 typeof 还支持类型保护,用于缩小联合类型的范围,从而在不同分支中得到不同的类型推断。

    typescriptCopy codefunction printLength(input: string | number) {
        if (typeof input === "string") {
            console.log(input.length); // 在这里,input 被 TypeScript 推断为 "string" 类型
        }
    }
    

in 关键字

intypeof有着很奇妙的共同点,他们都并不是 TypeScript 中新增的概念,而是 JavaScript 中已有的部分。您在从 js 迁移到 ts 时。会觉得这种使用方式是很符合直觉的,这里也不做过多介绍。简单说一下他如何做到类型推导作用。

首先我么回想一下 in 的作用

in 操作符用于检查一个对象是否具有特定属性。它返回一个布尔值,表示属性是否存在于对象中。

在 JavaScript 里,我们会这样使用。

const myObject = { name: "John", age: 30 };

console.log("name" in myObject); // 输出: true
console.log("gender" in myObject); // 输出: false

而在 TypeScript 中,in 操作符不仅可以用来检查属性是否存在,还可以用来进行类型推导、缩小联合类型的范围,从而更精确地推断变量的类型。而能让其发挥作用的,仅仅是一或多个可辨识属性(Discriminant Property 或 Tagged Property)

interface Person {
  name: string;
  age: number;
}

function printInfo(person: Person | string) {
  if ("name" in person) {
    // 在这里,TypeScript 推断 person 为 Person 类型
    console.log(`Name: ${person.name}, Age: ${person.age}`);
  } else {
    // 在这里,TypeScript 推断 person 为 string 类型
    console.log(`Name: ${person}`);
  }
}

instanceof 关键字

和前面两个‘前辈’ 一样,这里就不过多赘述了。

class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

class Animal {
  species: string;
  constructor(species: string) {
    this.species = species;
  }
}

function printNameOrSpecies(obj: Person | Animal) {
  if (obj instanceof Person) {
    console.log(obj.name); // 在这里,TypeScript 推断 obj 为 Person 类型
  } else if (obj instanceof Animal) {
    console.log(obj.species); // 在这里,TypeScript 推断 obj 为 Animal 类型
  }
}

总结

is 关键字

is 关键字不是 TypeScript 中的原生关键字,但您在代码中使用 is 来结合类型守卫,可以在条件分支中明确地告诉 TypeScript 一个变量的具体类型。这有助于在特定分支中进行精确的类型推断和类型检查。

typeof 关键字

typeof 关键字用于获取变量的数据类型,并且在 TypeScript 中可以用于类型检查和类型推断。您可以使用 typeof 关键字来获取变量的静态类型,或者用它来结合条件语句实现类型保护,缩小联合类型的范围,从而获得更准确的类型推断。

in 关键字

in 关键字用于检查对象是否具有特定属性。在 TypeScript 中,您可以使用 in 关键字结合可辨识属性(Discriminant Property 或 Tagged Property)来进行类型推断,缩小联合类型的范围,从而根据属性的存在与否进行精确的类型判断。

instanceof 关键字

instanceof 关键字用于检查对象是否是某个构造函数(或其派生构造函数)的实例。在 TypeScript 中,您可以使用 instanceof 关键字结合类型保护来进行更精确的类型推断,根据对象的实例关系判断变量的具体类型。

总的来说,这些关键字和技术在 TypeScript 中被用于不同的情境,可以帮助您在编写代码时实现更准确的类型推导和类型检查,从而提高代码质量和可维护性。

预告下篇文章 《初探泛型》

下篇文章中,我们将从泛型的基本概念出发,逐步深入探讨泛型的使用方法以及它如何在解决实际问题中发挥作用。从简单的泛型函数到更复杂的泛型约束、泛型类、泛型接口,我们将一一展开,为您呈现 TypeScript 泛型的精妙之处。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值