为什么需要TypeScript
- 解决JavaScript类型系统的问题
- 大大提高代码的可靠程度
思考:JavaScript 自有类型系统的问题(为什么有这些问题)?
先从编程语言类型系统了解
类型系统我们可以从两个维度来查看。一个是: 类型安全维度,就是看对类型在代码编译阶段束缚程度,有强类型和弱类型之分;另外一个是: 类型检查维度,就是对变量进行检查的强度。知道这些区别之后,结合javaScript当时的需求,为什么选择了弱类型动态类型系统。而现在前端工程为什么js不满足,而选择typescript
- 强类型与弱类型(类型安全)
-
强类型
语言层面限制函数的实参类型必须与形参类型相同
-
弱类型 在语言层面不会限制实参的类型
function foo () { console.log(num) } foo(100) foo('100') foo(parseInt('100')) -
由于这种强弱类型之分根本不是某一个权威机构的定义,也没有建立具体的规则,在业内没有同意标准
-
我的理解: 强类型有更强的类型约束,而弱类型中几乎没有什么约束
-
强类型语言中不允许任意的隐式类型转换
-
而弱类型语言则允许任意的数据原始类型转换
-
强类型是指语言的语法层面就限制了传入值的类型,如果传了不同类型的值在编译阶段就会报相应的类型错误,而不是等到运行阶段通过类型判断的代码去限制,在JavaScript当中所有报出的类型错误都是在源码代码层面的逻辑判断手动抛出,在运行阶段报错
“变量类型可以随时改变”这个不是强弱类型之间的区别,python是一门强类型语言,但是变量是可以改变类型的,但是会在编译层面报出类型相关错误。
- 静态类型与动态类型(类型检查) 静态类型
- 一个变量声明时它的类型就是明确的
- 变量声明过后,他的类型就不允许再修改
动态类型
- 运行阶段才能够明确变量类型
- 而且变量类型算是可以改变
动态类型语言中的变量没有类型的,变量中所存放值是有类型的
JavaScript 类型系统特征(其实算是一些对于现代前端工程而言存在的问题)
特征: 弱类型 且 动态类型
用一个词来描述javaScript类型系统就是 --- 任性。几乎没有任何的类型限制,导致js是及其灵活多变的,多变的表象背后缺失了类型系统的可靠性。
在js早期以脚本语言存在的时候,灵活解决问题直接运行确实满足了当时的需求且足够灵活,但是现在前端工程越来越大,越来越多的事可做,js语言的类型系统很“不靠谱”
为什么JavaScript不是强类型/静态类型
- 早前的javaScript应用简单(需求特别简单,只有几百行代码,或者几十行代码搞定)
- 在这种小需求,代码量一样看到头的情况下,类型系统的限制就会很多余,很麻烦。
- js是一门脚本语言,不需要编译就直接在运行环境中去运行,即便设计成静态语言也没啥意义,因为静态类型语言需要在编译阶段做类型检查。而JavaScript根本就没这个环节
基于以上三点,javascript就选择了更灵活更多变的弱类型/动态类型。
在大规模应用下,这种【优势】就变成了短板。不容易维护和迭代
杀鸡可以用小刀,小刀杀牛就难了。
弱类型的问题
- 在运行阶段才能发现代码异常错误
- 类型不明确会导致函数功能发生改变。
- 隐式转化。对对象索引器的用法存在不同,带来困扰
约定是没有保障的,只有使用强类型再回在编译阶段即可查出。如果在长期大规模的项目中,君子约定有隐患,强制要求有保障
强类型的优势
- 错误更早暴露(编译阶段)
- 代码更智能,编码更准确(与开发工具结合,自动提示和补全,不易犯错)
- 重构更牢靠(改了的变量名在编译阶段就会报出错误,你可以根据错误进行自动的修改)
- 减少不必要的类型判断(弱类型就需要用代码判断函数形参的类型来防止实参传进来的错误)
TypeScript
JavaScript的超集 (superset) 意思是在JavaScript原有的功能之上多了一些扩展特性,那多出来的就是一套更强大的类型系统和对ES6+新特性的支持。但是最终还是会被编译成JavaScript
/Users/zhenglinxiong/Desktop/前端学习/JS/assets/typeScript.png
所以我们在开发过程中使用TypeScript的话就可以使用提供的类型系统和ES6+新特性完成我们的开发工作,完成开发工作之后呢,再将代码编译成浏览器可运行的javaScript代码。
这样我们就很明显知道了TypeScript作用了,typeScript类型系统的优势在讲类型系统的时候也有些体会了
优点
- 避免在开发过程当中有可能会出现类型异常。然而提高我们编码的效率,以及我们代码的可靠程度
- 还可以更好的支持ES6+新特性,进一步提高了开发的生产力,如果直接使用JS新特性在一些陈旧的环境当中会不兼容会报错。但是ts会帮忙进行转化,处理了这些兼容问题,支持自动转化这些新特性。
- ts 会 编译成 任何一种JavaScript运行环境都能支持的代码(浏览器应用,node应用,ReactNative,Electronjs桌面应用)
- 功能更为强大,生态也更健全,更完善(特别是开发工具)
- 前端领域中的第二语言
- 属于渐进式的,什么ts特性后不知道,照js标准去写ts都没问题。了解一个特性使用一个特性
缺点
- 语言本身多了很多概念(接口,泛型,枚举),提高了学习成本
- 项目初期,TypeScript会增加一些成本(会写很多类型声明,周期短的羡慕不推荐使用TypeScript,大型的迭代久的反而这些是一劳永逸的)
使用TypeScript
- 初始化node工程
yarn init --yes - 安装typeScript编译器 typeScript
yarn add typescript --dev
// 安装好之后就可以,执行
yarn tsc typescript文件 // 进行编译了
- tsc这个命令不仅可以编译一个文件,还可以编译整个项目,或者说编译整个工程
编译整个项目之前呢,需要生成一个配置文件
yarn tsc --init
compilerOptions这里面都是可配置选项
- target // 将新特性转化为指定版本代码
- module // 输出方式
- outDir // 编译后的结果目录
- rootDir // 被编译的文件目录
- strict // 编译模式 为true是严格类型
- strictNullChecks // 检查变量不能为空
原始类型
string number boolean void null undefined symbol
标准库的声明
设置lib数组 lib: ["es2015", "DOM"] // 指定引用的标准库
作用域问题
会遇到不同文件相同变量名称这种情况!!!
使用export {} 语句将该文件转化为模块
ts Object 类型
- Object 类型不单指Object这一个类型,而是指除了原始类型其他都是Object 类型
- 可以用对象字面量申请,但是最好使用接口来实现
- object 不单指Object类型,指的是普通对象, 函数,数组
数组类型 Array Types
- 使用Array泛型
- 使用元素类型加[]进行类型声明
元祖类型 Tuple Types
明确元素数量以及元素类型,但是各个元素类型不必要相同的一种数据结构;
在React Hooks当中放回状态就是一个值 和 函数,有所应用;
枚举类型 Enum Types
我们在开发中会经常需要某几个数值代表某几个状态
const post = {
title: 'Hello TypeScript',
content: 'TypeScript is a typed supperset of JavaScript',
status: 2 // 1 // 0
}
这里有个文章状态 0 草稿 1 未发布 2 已发布。如果我们用 0 1 2字面量去表示发布状态,时间久了就会容易忘记而且时间长了就会混进来其他的一些值,这种情况下我们使用枚举类型时最合适的了。
枚举类型特点:
- 可以给一组数值取上更好理解的名字
- 一个枚举中只会存在几个固定的值,不会出现超出范围的可能性
在javaScript中只能使用一个状态对象去描述一组状态,ts有枚举类型可以直接使用
- 枚举值可以使用等号赋值
- 可以不使用等号,后面的值可以递增加1
- 枚举值也可以是字符串
- 推荐使用常量枚举值,枚举名不需要赋值 enum前加上const
const enum PostStatus {
Draft,
Unpublished,
Published
}
枚举类型会入侵到我们运行时的代码,影响我们编译后的结果。会编译成双向的键值对对象,这样的数据结构就是可以通过值获取键也可以通过键获取值。 通过索引值获取枚举名称,这样不是很好。我们可以使用常量枚举值
函数类型 Function Types
很显然函数类型就是需要对输入输出进行限制.
-
形参和实参在类型和个数,顺序上必须保持完全一致
-
函数表达式的方式声明函数,这里接收函数的变量也应该是有类型的,一般ts可以推断出这个变量类型,
-
如果我们是把一个函数当做一个参数去传递,这时候必须对这个参数有具体类型声明,这时候就需要用类似箭头函数的方式去 声明的函数类型(a: number, b: number, ...rest: number[]) => string
任意类型 Any Types
为了兼容javascript,比如JSON.stringify
any类型仍然属于动态类型,不会进行类型检查,所以存在类型安全问题,所以尽量使用。 但是兼容老代码的时候还是需要去兼容
隐式类型推断 Type Inference
如果我们没有明确的去通过类型注解去声明变量类型,ts会通过变量的使用情况去推导出变量类型。
- 按照变量第一次被赋值时的值类型推导
- 如果无法推断变量类型或者变量没有赋值,就会将类型推导为any
建议给每个变量定义类型,更易维护
类型断言 Type assertions
- 可以使用as和[<>变量名]进行断言
接口 interface
- 接口就是去约束对象的数据结构
- 在实际运行阶段是没有意义的
- 还可以定义接口成员是可选或者只读,动态
类 Classes
手机
智能手机
只能用类的实例
- TypeScript 增强了 class的相关语法
- 使用 public private(不可访问成员) protected(只允许子类访问成员)修饰符声明变量,控制成员访问级别
- 如果构造方法被私有化,那么创建类的实例就只能在类的内部生成
- 还可以是用readonly将成员为只读 在内部和外部都不允许被修改
- 类与类之间有一些共同的特征,这些特征我们可以把它抽象成接口(协议)座机打电话协议,收集打电话协议。多用,慢慢总结规律
动物会吃,人也会吃,但是这个吃却不一样,但是吃可以作为一个实现协议(接口)
- 一个接口最好就约束一个能力
- 然后让一个类去实现多个接口
抽象类
子类当中必须有某些成员
- 大的类目都使用抽象类(动物,车,手机)
- 接口只有声明没有实现,抽象类部分可以实现
泛型 Generics
定义函数 或 类 或 接口的时候,我们没有去指定特有的类型,等到我们使用的时候再去指定相关类型这样的一个特性叫做泛型。
- 目的: 极大程度的复用我们代码
- 在定义时不指定参数的类型,在使用函数时再传递参数
类型声明 Type Declaration
在项目搭建的时候我们都会使用不同的node模块这些模块不一定都是ts编写,如果想使用这些不是ts编写的模块,我们就必须做一个相应的声明
通常前端使用的比较热门的库都有相对应的类型声明下载
- 为了要考虑兼容js模块
- 类型声明文件下载放在开发模块中
- 类型声明文件是以d.ts结尾