TypeScript
首先第一个点,TypeScript 学起来并不难,大家要先从心态上不要太过于抗拒。
前端的所有知识基本上都是这样,学会了,就跟 1+1 等于 2 差不多,通透了非常简单,所以心态上要放好,如果心态上抗拒,基本上来说掌握起来就会变得困难。
第二个点,我尽量从学习方法的角度,去跟大家分享如何去学,而不是直接告诉你这个知识是什么样!所以大家在听课的时候,建议也是从这个角度去思考,最重要的是,要知道如何去学。
什么是 TypeScript
和其他语言不一样,JS 非常灵活,没有约束,所以基本上来说,别人的 JS 代码阅读起来是非常困难的。
就比如读源码,在前端行业是一个高逼格的事情,但是你学 Java,你会发现读源码就是一件非常自然,而且是大家都会去做的一个事情,因为 Java 的源码读起来非常方便。学 Java 的时候,大家都觉得读源码没什么了不起,就是因为强类型语言带来的好处。
TypeScript 就是给 JS 添加的一层语法规则,我们学 TypeScript,其实就是学一套语法规则。
小程序的 Taro 框架,大家估计都有耳闻,是我见过的,ts 用得最好的框架。大家可以看看,假设我现在对 Taro一无所知,是如何能够直接上手开发。
大家可以看一下,假设 View 组件,我们事前一无所知,通过 ts,就可以很快速的知道它由那些 props 能够使用,只要对 react 熟悉,基本上来说,这种新的 UI 组件用起来是没有任何障碍的。
所以,在 ts 的声明文件用得非常好的情况,大家学会了 ts,基本上 Taro 也能轻松无压力的使用。
ts 还帮助我们快速的了解一个变量的类型,一个函数的入参与出参。
这一点非常重要,只要在写代码的时候类型声明做得好,那么再维护代码的时候就非常轻松。重点就在于维护简单!!
经常改bug的同学应该会体会比较深刻。如果出了一个bug,正常的只用js开发,很难肉眼就把bug抓出来,我们以前要借助各种调试
现在我改bug的一个感受,很多bug基本上通过bug现象,和数据类型,大概就能猜到哪里出问题,也能够很方便的读懂其他同事的代码。
这就是可维护性。我们说代码质量,在团队协作中,可读性和可维护性一定是占最高优先级的。
然后就是其他的一些边边角角的优点,大家都比较熟悉了,都在说,比如写代码的时候直接能够发现错误啊,代码提示啊等等。但是typescript的使用会带来额外的代码量,这个有的人会接受不了。这个额外的工作量并不小。
就比如说,antd pro的官方demo,基本上每个页面都会用很长的一段类型声明来描述每个数据类型。任何一个用到的数据,都必须有对应的声明描述。你不用的话,就不是在用typescript了。
根据我的经验,这不会影响工作进度,不会因为typescript导致你加班多久多久。反而是因为一个小bug,有可能你会加班一晚上甚至一整天。
基本上来说这种数据声明,其他的语言都逃不掉。例如Android开发,swift开发,包括Flutter的Dart开发。都逃不掉对进行类型声明,所以虽然多出来一些工作量,但不会影响进度,并且其他开发都会做,所以大家不要因此心生抵触。
TypeScript 在项目中的基本使用
第一种常见的方式,就是针对以前已经用 js 代码写好的方法啊、模块呀、框架啊等进行描述。因为以前的代码很多不是用 JS 写的,但是又要享受 ts 带来的便利,所以 ts 经过很多个版本的迭代,找到了一种比较好的方式来描述已经存在的代码。
这种单纯的描述文件,都以 .d.ts
结尾。另外一种,就是可以存在于任何 .ts
、.tsx
文件中。
以一个 React 的组件来举例,我们一个页面组件包含的元素大概如下:
index.tsx
api.ts 数据请求
interface.d.ts 该页面可能会用到的数据的类型描述
commonets 子组件
model.ts 数据管理中心
index.scss 样式表
images 图片资源
比如我们公司的一个项目,用 react native 写的,一个页面组件包含的东西:
当然也不是绝对的,如果一个组件需要的描述类型比较少,就不用单独写在一个文件里。我的做法是直接在 api 文件里声明:
例如这个组件,只有一个接口,只有一份数据需要单独描述,根据实际情况灵活来做就行。
那么剩下的东西,就是如何使用 TypeScript 支持的语法,来描述我们在实践过程中遇到的数据了。
怎么描述,主要的就是看官方文档,我带着大家一起过一下,大家下来要花一个小时详细阅读。描述基础数据类型,很简单。
官方文档的语法,是在ES6的基础上来扩展的,官方文档也认为 ES6 是自己的语法之一,所以官方文档有许多 ES6 的知识点。
如果对 ES6 比较熟悉,这些知识点基本上可以忽略。
这几个点一个一个过一遍。
1. 基础类型
基础类型非常简单,基本上来说大家自己都能学得会,需要注意的一个点就是类型断言。
实践中一般我们都只用 as 语法,意思就是 强制类型约束
例如本来 10 是一个数字,但是我强制要求它变成一个 string,然后ts规则就会认为 a 是一个字符串。
这个就是在实践里,我们常常会在ts能力达不到的地方使用。具体要用的时候去感受,言语很难说清楚。
2. 变量声明
官方文档第二个:变量声明。说的是 ES6 的 var/let/const 这些东西,忽略,大家自己去看。
3. 接口
官方文档第三个:接口。对应的是大部分后端语言里的概念 interface。
在 JS 里,就是用来描述 JSON 数据的。
例如,在 antd pro 官方文档中,有一个 JSON 数据长这样:
它的描述接口
就长这样。就很简单,基本上一一对应。
然后就是一些语法上的知识,例如可选啊,只读啊,继承啊等等。
这个大家自己阅读文档学习。
但是我们在使用 JSON 数据的时候都知道,有很多非常刁钻的场景。例如我们也不知道这个 JSON 对象会有哪些字段,可能是这几个,也可能会多加几个。
这种情况就使用可索引的类型来描述。
4. 类
官方文档第四点:ES6 的类 —— class,略过。
5. 函数
官方文档的第五点:函数。
有很多 ES6 的知识,然后另外一个要描述清楚,这个函数传入的参数是什么,以及返回的数据类型是什么
然后其他的就是 ES6 的东西,不再多说,略过。
6. 泛型
官方文档第六点,泛型。整个 TypeScript,这是第一个难点。基本上来说,懂了泛型,其他的就不会是问题。
因为有类型约束,所以 TypeScript 失去了很多灵活性。但是有的时候,我们又希望数据类型具备一定的灵活性。但是这种灵活性,又想他带有一定的约束。所以就产生了泛型这个语法。
以数据请求为例,在 antd pro 的官方案例中,请求的函数 request:
我们知道这种异步请求函数返回的结果,一定是一个 Promise 对象,每一个接口请求都是返回的 Promise。但问题就在于,每个接口,返回的数据肯定是不一样的,但是我们在具体使用之前,谁也不知道具体的场景里返回的数据是什么。所以这里就用泛型来表示一个变量,既然我不知道是什么,那我就用一个变量来占位。所以返回结果的描述,就变成了 Promise<T>
。
我们在使用函数时,传入这个变量,ts 就能够推导出来返回结果是什么:return request<AnalysisData>('/api/fake_chart_data');
request 在使用时传入了变量 AnalysisData ,这个变量时一个数据的类型声明,声明的就是返回结果
[图片上传中...(image-20191206171045694.png-df7a12-1575641450209-0)]
我们在约定请求返回结果时,也会用到泛型,例如,我们知道返回结果里,一定会有是否成功的一个字段,success,也一定知道还有一个字段,data。但是我们不知道 data 的具体类型,因此用泛型来声明返回结果。
到具体的场景里,data 是什么,我们就传入什么。
这个就是泛型,理解了很很简单。
关于泛型的解释,官方文档反而弄复杂了,所以如果我说的听懂了,就可以不用看官方文档。
7. 枚举
枚举:就是一个简单的枚举语法,由于在前端里,1个是我们需要的字段有三个,枚举表示不了,key,value,以及对外显示的字符串,所以前端逻辑里用枚举比较少,略过。
8. 类型推导
官方文档第八个知识点:类型推导
类型推导就是根据已经存在的条件,自动推导出来数据的数据类型是什么。因为有自动推导,我们可以在很多地方不用写数据类型声明。
例如这个例子,useState 的初始化数据类型已经很明确,非常容易就推导出来,那么 store 的类型就不需要再额外声明一个。
即使没有声明,提示也准确的告知了 store 的类型格式。这个是 TypeScript 自身的特性,使用的时候,我们发现能自动推论出来时,不去声明就 OK,这一点略过。
9. 类型兼容性
官方文档第九点:类型兼容性。就是什么样的类型能够赋值给什么样的类型。
例如 a = 10, b = 'hello'
,类型不同,a 不能赋值给 b,完全不兼容。但是当 a, b 相同时,就能相互赋值。
我们在使用时,经常看到这类报错提示:
实践中这类提示非常多,大家肯定会遇到,这就表示,你想要赋值的两个数据类型不兼容。当完全相同和完全不同时,大家非常容易理解。但是当包含关系时,就需要注意:
两个类型声明类似,但是 Person 两个值,都是可选的
我想要将 p1: Person 赋值给 p2: Person2 时,不兼容,因为 p2 的值必须有,但是 p1 可以没有。所以 p1 不能赋值给 p2,会出现异常情况。
他最终的错误提示,就是说 undefined 不能赋值给 string,其实就是 name 的属性不兼容。但是反过来,就可以:
就不会因为类型不兼容报错。
这个知道报错的原因,就行了,大家不用过于在意这个点,官方文档略读即可,因为你使用时出错了会告诉你。
10. 高级类型
官方文档第 10 个知识点:高级类型
这个高级类型,大家要作为 TypeScript 的进阶学习来看待,初学时可以不用太在意。例如你 TypeScript 用过一段时间,比较熟悉了,再来学习他,我们在实践里,大多数情况下用到比较少,不过会用于解决一些非常复杂的数据类型。
我的建议是暂时略过,再理解了这些高级用法存在的必要之后,再专门花一个下午的时间来专项攻克。具体的知识点,官方文档说的很清楚,所以本次分享略过。
但是大概读一下也可以,有一些非常简单的,也有一些非常复杂的,还有许多框架层面的东西,经常用到这些高级语法。
11. Symbols
ES6 的知识点,略过。
12. 迭代器生成器
ES6 的知识点,略过。
13. 模块
ES6 的知识点,略过。
14. 命名空间
在一个专门的声明文件里,.d.ts
我们可以这样,直接导出声明,不过使用时,需要引入才能用
我们也可以专门用一个命名空间包裹起来
那么在项目中其他文件中使用时,直接用即可,不需要引入 test2
这个就是命名空间,很简单,关键的地方在于命名空间的名字必须是唯一的。
15. 模块声明
然后就是模块声明,declare module ‘react’
它的作用就是我们刚才说的,使用声明文件去描述一个已经存在的js库时,常常用到的语法
要注意它与命名空间的差别
直接这样使用即可。
16. 模块解析
ES6 的知识点,略过。
17. 声明合并
我实践里面用得很少,有点忘了,感兴趣的可以去阅读以下,略过。
18. React的结合使用
待会儿说。
19. 装饰器
ES7 知识点,略过。
20.
我几乎没用过,参考阅读,略过
21.
我几乎没用过,参考阅读,略过
你看,其实官方文档没有那么复杂,只要梳理清楚,基本上两个小时,typescript就掌握了。关键的就是心态。不要因为新增一个东西,就觉得压力很大,其实掌握了学习方法,这些新出的东西都是纸老虎。
如果你自己给自己太大的压力,反而是障碍。
---------------提问环节的回答--------------
tsx是 react文件,
ts就是js文件
.d.ts是专门的类型声明文件,不能写JS逻辑
interface就是接口,但是在TS里没有具体的逻辑上的含义,就是用来描述数据类型,但是也可以继承等
泛型不能是any的,因为泛型有一个简单的约束,就是传入的值和需要的值类型要保持一致。
any就是完全没有约束。
有泛型就表示有约束啊,any没约束,默认值那个就是为了干掉报错。
typescript的依赖反正都是自动引入的,用的时候自己可以关注一下变化,namespace在有依赖的时候,会自动import进来。
泛型没有什么特别,就没了呀,就只要一个用法。至于嵌套泛型啊,什么的,这些都是你实际用的时候,自然而言就能够感受到的,我觉得没必要去可以强化它的复杂度。
https://www.tslang.cn/docs/handbook/mixins.html 大家下来还是要过一次官方文档,按照我说的,最多2个小时就够了
有一个心态要明确的就是,并不是说官方的,就一定是最佳实践
就包括react团队,虽然他们把这个框架开发出来了,但是他们不一定是用得最好的
所以针对这些新出的东西,理解了基本使用之后,要有自己的思考
包括react他们自己也在不断的纠正自己的缺点,所以这个东西都是不断进步的
React
首先react 其实没什么特别的,就是正常的使用,以函数式组件为例,其实就是函数的用法。
例如这个函数组件,其实就是声明传入的参数数据类型。
然后class语法也是一样。
用了两个泛型变量,分别表示当前组件的props类型和state类型。
他内部的声明就很简单,所以class语法声明组件也不复杂。
两个泛型,唯一特殊的地方就是JSX,也就是组件。在使用时,属性和props是对应起来的。
这些传入的属性,就是props声明的类型。
然后就没有了,react 就这样简单。
因为有typescript来声明约束props,所以之前react提供的校验方式就可以不用了。
这个东西,可以不用了。