TypeScript 系列(一):TS 核心概念

1,103 阅读6分钟

1 前言

昨天在掘金上看到了一篇 TypeScript5,是的,TS 都更新到 5 了,我还没开始正式使用。为了赶上时代的潮流,我尝试在自己写的小项目中使用了 TS,然而,直接裂开……于是就有了这个系列,我准备攻克这门技术,并把所思所想记录下来。

image.png

我打开了 TS 英文文档,等一下,如果你要问我,为什么打开英文文档,我想说我也很懒想看中文文档,可是中文文档有两点弊端:

  • 中文更新不及时
  • 存在部分内容不翻译
  • 有些内容翻译过来就变了味

作为一个前端程序员,在 Get Started 中我选择 TypeScript for JavaScript Programmers。 这一篇教程告诉我们一件事:JS 的语法 TS 全有,TS 比 JS 多了一层类型系统(Type System):

  • 类型推断(Types by Interface)

  • 类型定义(Defining Types)

  • 类型组合(Composing Types)

    • 类型联合(Unions)
    • 泛型(Generics)
  • 结构化类型系统(Structural Type System)

以上就是 TS 与 JS 主要不同的地方,也是 TS 的主要概念,我们一个一个看。

2 类型推断(Types by Interface)

image.png

Types by Interface,通过推断而产生类型。啥意思呢?就是你和平时写 JS 一样去写 TS 的变量声明和赋值,TS 会根据值的类型去推断你声明的变量属于什么类型。例如:

image.png

1 被赋值给变量 a,1 属于基础类型中的 number 类型,因此显而易见的是,变量 a 的类型就是 number。

这里可能会产生一个疑问:TS 帮我们推断变量类型的意义是什么?为了让我们像静态语言那样,固定变量的类型,从而减少不必要的 bug。

例如,在下方代码片段中, 变量 result 显式声明为 number 类型,然而 a + '1' 的结果是字符串 '11',不符合要求,这时 TS 的类型检测系统就会帮你发现问题,提示你出现错误了!

image.png

而 JS 一直以来都是一个动态类型语言,它的变量类型可以随意改动,于是乎,就会造成这样的结果:

image.png

3 类型定义(Defining Types)

TS 的第二个主要概念是类型定义,在 JS 当中会出现复杂的代码实现,这些代码的类型是很难被推断的。所以,TS 提供了接口(interface)的概念用来描述这些复杂的定义。

3.1 字面量对象

例如,定义一个对象的接口 Person,这个对象接口含有 nameage 两个属性,name 的类型必须是 stringage 的类型必须是 number。定义完成后,第 6 行的对象 p 的类型就是 Person

image.png

如果字面量对象与接口定义不一致,那么就会报错:

image.png

这就像口袋里只能放一只红球和一只蓝球,可你放了一只黄球和一只蓝球,当然不符合规则。

3.2 class

TS 也支持 class 语法和 OOP。

与字面量对象一样,如果传递的参数不符合规则,那么就会报错:

image.png

如果在没有在一开始定义构造器属性的类型,那么也会报错:

image.png

3.3 函数参数和返回值

TS 可以给函数的参数和返回值定义类型。

  • getPerson() 之后的 Person 表示返回值需要符合 Person 类型;
  • deletePeople(person: Person) 中的参数 person 需要符合 Person 类型。

image.png

3.4 TS 中的基础数据类型

除了 JS 的 7 种基础数据类型(string, number, bigint, boolean, undefined, null, symbol),TS 还拓展了 unkownanynevervoid 四种数据类型。

  • unknown,未知类型,在你使用它的自行定义类型
  • any,任意类型
  • never,不可能到达的类型
  • void,函数无返回值的类型

4 类型组合(Composing Types

使用 TS 可以将简单的类型组合成复杂的类型,一共有两种方案:

  • 一个是 union,叫做类型联合
  • 一个是 generics,叫做泛型

4.1 Union

类型联合是啥意思呢?就是某个类型可能是 A,可能是 B,可能 C。

类型联合以 type 开头来定义。

image.png

4.2 Generics

泛型给类型提供变量,让类型更加灵活,这就类似于“普通函数从没有入参到带了入参”的跃迁。

image.png

  • Array<string> 表示这是一个由 string 类型数据组成的数组;
  • Array<number> 表示这是一个由 number 类型数据组成的数组;
  • Array<{name: string}> 表示这是一个由接口对象 {name: string} 数据组成的数组。

可以自己定义泛型,然后使用。举个例子,定义一本书的接口,书的价格 price 是由外部传入的(指的是 Type),addPrice 的入参类型、getPrice 的返回值类型也全都是外部传入的。

image.png

第 9 行声明了一个 book1 的变量,它的类型是 Book<string>,因此:

  • book1.price 类型就是 string
  • book1.addPrice() 中的 name 参数的类型就是 string
  • book1.getPrice() 中的函数返回值类型就是 string

所以,book2 的这三个类型又是什么呢?欢迎在评论区回答👏,哈哈。

5 结构类型系统(🦆 Structural Type System)

结构型类型系统,又称为 duck typing,鸭子类型。啥意思呢?只要它长得像鸭子,那它就是鸭子。

5.1 鸭子类型

image.png

对应的,在代码层面,只要这个对象包含了符合某个类型的属性并且类型相同,那么就说这个对象属于当前这个类型。换句话说,如果对象里有其他的属性存在并不会影响到对象的类型归属。

image.png

  • 显然,d1 符合 Duck 类型
  • d2 中含有 namecolor 属性并且类型符合 Ducknamecolor,但是 d2 还有一个 age 属性,这个属性其实是 Duck 没有的,但它仍然属于 Duck
  • d3 实际上和 d2 是一样的,但是却报错了。原因在于,对象字面量只能和你定义好的属性配对上,但是 age 不在其中。

那么,鸭子类型的意义是什么呢?当前后端联调时,前端通过接口拿到后端传递来的数据,这个数据一般就是 response.data.data ,这里面的数据结构并不一定和我们定义的接口类型完全一致,这时报错当然不符合情理。

5.2 class

在 class 中规则也是相同的。

image.png

6 总结

  • TS 是 JS 的超集,在 JS 上架了一层类型系统
  • TS 可以帮你自动推导出变量的类型
  • 遇到比较复杂的对象,你可以使用接口 interface 来定义类型,其实就是手动推断(显式推断)
  • 基础数据类型、字面量对象、class、函数参数和返回值,你能想到的标识符名称都能被赋予类型,还有 TS 特有的四种类型:unkownanynevervoid
  • 如果需要将基础数据类型组合在一起,就使用类型联合
  • 如果想要动态改变接口类型,那么就使用泛型
  • 鸭子类型的理解很重要,有助于我们更好地写接口

TS 文档里其实还有很多内容,难以做到面面俱到,那么我们就从核心概念开始~另外,这篇文章比较偏概念,实践相关的内容将在后续章节呈现。

添加我的微信:with_his_x,共同成长,卷卷群里等你 🤪。

以上,如有谬误,还请斧正。

最后,希望这篇文章对你有所帮助,感谢您的阅读~