昨天在掘金上看到了一篇 TypeScript5,是的,TS 都更新到 5 了,我还没开始正式使用。为了赶上时代的潮流,我尝试在自己写的小项目中使用了 TS,然而,直接裂开……于是就有了这个系列,我准备攻克这门技术,并把所思所想记录下来。
我打开了 TS 英文文档,等一下,如果你要问我,为什么打开英文文档,我想说我也很懒想看中文文档,可是中文文档有两点弊端:
作为一个前端程序员,在 Get Started 中我选择 TypeScript for JavaScript Programmers。 这一篇教程告诉我们一件事:JS 的语法 TS 全有,TS 比 JS 多了一层类型系统(Type System):
类型推断(Types by Interface)
类型定义(Defining Types)
类型组合(Composing Types)
结构化类型系统(Structural Type System)
以上就是 TS 与 JS 主要不同的地方,也是 TS 的主要概念,我们一个一个看。
Types by Interface,通过推断而产生类型。啥意思呢?就是你和平时写 JS 一样去写 TS 的变量声明和赋值,TS 会根据值的类型去推断你声明的变量属于什么类型。例如:
1 被赋值给变量 a
,1 属于基础类型中的 number 类型,因此显而易见的是,变量 a
的类型就是 number。
这里可能会产生一个疑问:TS 帮我们推断变量类型的意义是什么?为了让我们像静态语言那样,固定变量的类型,从而减少不必要的 bug。
例如,在下方代码片段中, 变量 result
显式声明为 number 类型,然而 a + '1'
的结果是字符串 '11'
,不符合要求,这时 TS 的类型检测系统就会帮你发现问题,提示你出现错误了!
而 JS 一直以来都是一个动态类型语言,它的变量类型可以随意改动,于是乎,就会造成这样的结果:
TS 的第二个主要概念是类型定义,在 JS 当中会出现复杂的代码实现,这些代码的类型是很难被推断的。所以,TS 提供了接口(interface
)的概念用来描述这些复杂的定义。
例如,定义一个对象的接口 Person
,这个对象接口含有 name
和 age
两个属性,name
的类型必须是 string
,age
的类型必须是 number
。定义完成后,第 6 行的对象 p
的类型就是 Person
。
如果字面量对象与接口定义不一致,那么就会报错:
这就像口袋里只能放一只红球和一只蓝球,可你放了一只黄球和一只蓝球,当然不符合规则。
TS 也支持 class
语法和 OOP。
与字面量对象一样,如果传递的参数不符合规则,那么就会报错:
如果在没有在一开始定义构造器属性的类型,那么也会报错:
TS 可以给函数的参数和返回值定义类型。
getPerson()
之后的 Person
表示返回值需要符合 Person 类型;deletePeople(person: Person)
中的参数 person
需要符合 Person
类型。除了 JS 的 7 种基础数据类型(string
, number
, bigint
, boolean
, undefined
, null
, symbol
),TS 还拓展了 unkown
、any
、never
、void
四种数据类型。
unknown
,未知类型,在你使用它的自行定义类型any
,任意类型never
,不可能到达的类型void
,函数无返回值的类型使用 TS 可以将简单的类型组合成复杂的类型,一共有两种方案:
类型联合是啥意思呢?就是某个类型可能是 A,可能是 B,可能 C。
类型联合以 type
开头来定义。
泛型给类型提供变量,让类型更加灵活,这就类似于“普通函数从没有入参到带了入参”的跃迁。
Array
表示这是一个由 string
类型数据组成的数组;Array
表示这是一个由 number
类型数据组成的数组;Array<{name: string}>
表示这是一个由接口对象 {name: string}
数据组成的数组。可以自己定义泛型,然后使用。举个例子,定义一本书的接口,书的价格 price
是由外部传入的(指的是 Type
),addPrice
的入参类型、getPrice
的返回值类型也全都是外部传入的。
第 9 行声明了一个 book1
的变量,它的类型是 Book
,因此:
book1.price
类型就是 string
book1.addPrice()
中的 name
参数的类型就是 string
book1.getPrice()
中的函数返回值类型就是 string
所以,book2 的这三个类型又是什么呢?欢迎在评论区回答,哈哈。
结构型类型系统,又称为 duck typing,鸭子类型。啥意思呢?只要它长得像鸭子,那它就是鸭子。
对应的,在代码层面,只要这个对象包含了符合某个类型的属性并且类型相同,那么就说这个对象属于当前这个类型。换句话说,如果对象里有其他的属性存在并不会影响到对象的类型归属。
d1
符合 Duck
类型d2
中含有 name
,color
属性并且类型符合 Duck
中 name
和 color
,但是 d2
还有一个 age
属性,这个属性其实是 Duck
没有的,但它仍然属于 Duck
。d3
实际上和 d2
是一样的,但是却报错了。原因在于,对象字面量只能和你定义好的属性配对上,但是 age
不在其中。那么,鸭子类型的意义是什么呢?当前后端联调时,前端通过接口拿到后端传递来的数据,这个数据一般就是 response.data.data
,这里面的数据结构并不一定和我们定义的接口类型完全一致,这时报错当然不符合情理。
在 class 中规则也是相同的。
unkown
、any
、never
、void
TS 文档里其实还有很多内容,难以做到面面俱到,那么我们就从核心概念开始~另外,这篇文章比较偏概念,实践相关的内容将在后续章节呈现。
添加我的微信:enjoyMrcat,共同成长,卷卷群里等你 。
以上,如有谬误,还请斧正。
最后,希望这篇文章对你有所帮助,感谢您的阅读~