• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

三个经典的TypeScript易混淆点

武飞扬头像
Justin3go
帮助81

前言

  • 本文会讲什么:主要讲解TypeScript在开发过程中的易混淆点,当然也同样是面试官常考的几个题目
  • 本文不会讲什么:本文并不是又大又全的TypeScript学习教程,不会讲那些基础知识、简单概念等,比如JS的内置类型这类。所以如果你是新手玩家,最好先去做一下新手任务出了新手村再这里

你知道interface与type有什么区别吗

官网这里有较为详细的介绍,并且提到一句类似于最佳实践的话:

简单来说就是能用interface就用interface,除非Typescript提醒你了或者是interface根本实现不了这个功能。

具体来说它们有如下重要的区别

主要区别

type定义之后就不能重新添加新的属性;而interface则是始终可以扩展的;即仅inyterface支持合并类型

这里简单叙述一下官网中的例子:

interface Window {
  title: string
}  
// 这步是OK的,`ts: TypeScriptAPI`就合并进入了之前定义的Window这个接口
interface Window {
  ts: TypeScriptAPI
}

const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {});

而基于已经定义的type继续添加新的字段就会Error

type Window = {
  title: string
}  
type Window = {
  ts: TypeScriptAPI
}  
 // Error: Duplicate identifier 'Window'.

其他区别:interface的限制

前面提到能使用interface的时候就使用interface,除了interface实现不了你想要的功能的时候。那本小节就描述一下interface又有什么限制:

不能直接操作基本类型(如stringnumber这些)

比如如下代码放在编译器中就会报错,因为extendsstring这个基本类型:

interface X extends string {  // error
 // ...
}

type则可以,如下是大家可能经常使用的操作:

type stringAlias = string;  // ok
type StringOrNumber = string | number;  // ok

本章小节

  • interface > type:合并类型
  • type > interface:操作基本类型

当然,基于已有知识如JavaScript进行联想,你可以简单理解为type == constinterface == class。这种理解也许有点片面,不过仅仅是为了方便记忆...

你知道never类型是用来干什么的吗

定义

故名思义,never是一种表示永远不会出现的类型,那什么是永远不会出现的类型呢,比如当一个函数陷入无限循环或者抛出异常时,我们就可以把这个函数的返回类型定义为never

如:

function throwError(message: string): never {
  throw new Error(message);
}

注:never 类型仅能被赋值给另外一个 never

应用场景1

对于平常开发中,never相对来说可能是使用的较少的了。更多人可能只是知道其定义,但不知道其场景/作用。

第一个场景就是前面举例提到的定义无返回的函数的返回类型。当然,除了上述中抛出异常会导致函数无返回,还有种形式是产生了无限循环导致代码执行不到终点:

function infiniteLoop(): never {
  while (true) {
    console.log("justin3go.com");
  }
}

我们可以思考一下:没有never时会导致什么坏情况出现

总的来说,never可以帮助 TypeScript 编译器更好地理解这个函数的行为,并在代码中进行类型检查。

例如,下面这个函数会抛出一个异常,表示输入的值不是一个有效的数字:

function parseNumber(value: string): number {
  const result = Number(value);
  if (isNaN(result)) {
    throw new Error(`${value} is not a valid number.`);
  }
  return result;
}

如果我们尝试调用上述这个 parseNumber() ,但是传递了一个无效的字符串参数,TypeScript 编译器无法识别这个函数会抛出一个异常(此时是假设的没有never类型)。而此时它会将函数的返回类型设置为 number,这会导致一些类型检查错误。

比如一个大型系统中我们调用这个通用函数时,而仅仅看到了TS的提示说这个函数返回的是一个number,然后你就非常笃定其返回值是一个number,于是就基于这个number类型做了许多特别的操作,哦豁,后续很可能出现偶发性bug。

而当有了never类型,我们就可以设置为这样:

function parseNumber(value: string): never | number {
  const result = Number(value);
  if (isNaN(result)) {
    throw new Error(`${value} is not a valid number.`);
  }
  return result;
}

现在,如果我们尝试调用 parseNumber 函数并传递一个无效的字符串参数,TypeScript 编译器会正确地推断出函数会抛出一个异常,并根据需要执行类型检查。这可以在我们编写更安全、更健壮的代码时提供非常好的帮助。

应用场景2

在 TypeScript 中,never 类型可以用作类型保护。因为如果一个变量的类型为 never,则可以确定该变量不可能有任何值。例如下方这个经典例子:

function assertNever(x: never): never {
  throw new Error("Unexpected object: "   x);
}

function getValue(x: string | number): string {
  if (typeof x === "string") {
    return x;
  } else if (typeof x === "number") {
    return x.toString();
  } else {
    return assertNever(x);
  }
}

这里,我们在最后一个分支使用never类型做了兜底,如果不使用never,这里TS检查就可能报错,因为最后一个分支没有返回与函数返回值为string相互冲突:

void的区别

刚才那个例子其实我们这样避免报错(当然并不推荐,这里仅仅为了引入void):

  • 这样,就不会报错了,因为void表示该函数没有返回值,所以string | void1兼容了所有的分支情况,但是这里非常不推荐这么做,正确的做法还是assertNever那个例子
  • 原因是如果我们对这个函数按照参数类型正确传递参数,是不可能走到最后一个分支的,所以也就没必要单独在或一个void了,这样反而会误解这个函数的意思,增加操作;
  • 此时使用抛出异常这个never类型就可以既避免该函数的返回值检查,又可以做一个兜底,在后续确实传参错误的时候抛出异常以避免执行后续的代码

所以,那voidnever的区别是啥?

  • void:整个函数都正确执行完了,只是没有返回值
  • never:函数根本没有执行到返回的那一步

本章小节

never是一种表示永远不会出现的类型,主要在以下两种场景中使用:

  1. 无法执行到终点的函数的返回类型应设置为never
  2. 可以用作类型保护

其中,无法执行到终点 与 在终点不返回是两个意思。这也是nevervoid的主要区别。

你知道unknown和any之间的区别吗

概括

首先你可以将unknown理解为TS认可的一种类型,它确确实实是TS内置的一种类型;而any你可以理解为它是为了兼容JavaScript而出现的一种类型,与其说是兼容JavaScript,不如说是兼容那些不太会TypeScript的程序员。当然,有时候项目赶工确实很着急那也没办法...

unknown简述

unknown表示一种不确定的类型,即编译器无法确定该变量的类型,因此无法对该变量执行任何操作。通常情况下,unknown类型的变量需要进行类型检查或者类型断言后才能使用。例如:

let userInput: unknown;
let userName: string;

userInput = 5;
userInput = 'hello';

// 需要进行类型检查
if (typeof userInput === 'string') {
  userName = userInput;
}

// 或者需要进行类型断言
userName = userInput as string;

any简述

any表示任意类型,即该变量可以是任何类型。使用any 类型会关闭 TypeScript 的类型检查,因此使用 "any" 类型时需要小心,因为它会导致代码中的类型错误难以被发现。例如:

let userInput: any;
let userName: string;

userInput = 5;
userInput = 'hello';

// 没有类型检查
userName = userInput;

最后

其他两个问题也相继问了一下,有些帮助,但也仅此而已;这里疑惑的是我既然参考了它的回答,那我该不该引用它呢?如果引用它,那它的知识又来自于互联网,它自己却没有注明知识来源处...

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanfekac
系列文章
更多 icon
同类精品
更多 icon
继续加载