简介
什么是TypeScript TypeScript 是 JavaScript 的超集,其包含了 JavaScript 基本语法也具有额外扩展
TypeScript 是一个类型检查工具,给我们语法提示
强类型语言和弱类型语言 强类型语言和弱类型语言,是从类型安全的角度去判断的
强类型语言
强类型语言中不允许有任意的隐式类型转换
强类型语言
弱类型语言中允许任意的数据隐式类型转换
静态类型语言和动态类型语言 静态类型语言和动态类型语言,是从类型检查的角度去判断的
静态类型语言
变量声明时,它的类型是明确的,声明之后,变量的类型就不允许再修改
动态类型语言
在运行阶段才能明确变量类型,而且变量的类型随时可以改变。
也可以理解为,变量是没有类型的,变量中存放的值是有类型的
Flow
说明
JavaScript 的类型检查器
通过给变量添加注解的方式,给变量标注是什么类型,通过注解在开发阶段检查变量类型是否存在异常
只是在编码阶段进行使用,运行阶段需要删除注解,否则无法运行
1 2 3 4 5 6 7 function sum (a :number,b :number) { return a+b }sum (100 ,"100" )
添加类型注解
npm 安装 npm i flow-bin -D
在需要使用 flow 进行检查的文件中添加注释标记 //@flow
给变量添加类型注解
在保存时会自动进行类型检查
index.js 文件
1 2 3 4 5 6 7 8 function sum (a :number,b :number) { return a+b }sum (100 ,100 )sum ("100" ,"100" )
编译
说明
编译的目的是为了移除类型注解,因为类型注解只在代码编写过程中用来检查类型,代码运行是无法识别类型注解的,所以需要移除
移除方式
flow-remove-types
npm 安装 npm i flow-remove-types -D
运行 npm run flow-remove-types 指定目录a -D 指定目录b 将指定目录a下的文件编译到指定目录b
babel
通过 babel,并添加 preset-flow 插件进行编译
vscode flow插件 可以直接在代码中直接显示类型异常,不需要看控制台
插件名 Flow Language Support
类型推断 变量没有使用类型注解,但是在代码编写中变量的使用可能进行隐式类型转换,此时 flow 也会提示错误
类型注解 变量
在变量后面使用 :type 的形式进行类型注解
1 2 3 4 5 let a :number = 12 function sum (a :number,b :number){ return a+b }
函数
在函数体之前使用 :type 定义类型注解,用于定义函数返回值类型
函数没有返回值,类型注解定义为 :void
1 2 3 function sum (a,b):void { return a+b }
注解类型 flow官网类型文档:https://flow.org/en/docs/types flow第三方类型手册:https://www.saltycrane.com/cheat-sheets/flow-type/latest/
基本类型
1 2 3 4 5 6 let a :string = "aaa" let b :number = infinity let c :boolean = true let d :null = null let e :void = undefined let f :symbol = Symbol ()
数组
1 2 3 4 5 let a :Array <number> = [1 ,2 ,3 ] let b :number[] = [1 ,2 ,3 ]let c :[string,number] = ["aa" ,12 ]
对象类型
1 2 3 4 5 6 7 let a :{for :string,bar :number} = {for :"aa" ,bar :12 } let a :{for ?:string,bar :number} = {for :"aa" ,bar :12 } let c :{[string]:string} = {} c.key1 = "value1" c.key2 = "value2"
函数类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function sum (a :number,b :number) { return a+b }function sum (a,b):number { return a+b }function sum (cal :(string,number )=> void ){ cal ("aa" ,100 ) }
特殊类型
字面量类型
限制变量必须接收某一个固定值
联合类型
限制变量只能接收多种类型或字面量类型中的某个类型或某个值
1 2 let a :"foo" |"abb" :"bcc" = "bcc" let b :number|string = 10
类型别名
给注解的类型定义为一个类型变量,方便调用和配置
1 2 type abc = number|string let a :abc = 10
maybe类型
使变量除了可以赋值类型注解规定的类型值,也可以赋值 null 和 undefined
mixed类型 和 any类型
mixed 和 any 都规定变量可以接收任意类型的数据
mixed:属于强类型控制,当编写代码中使用 mixed 注解变量,调用此变量进行可能造成隐式类型转换的操作都将报错
any:属于弱类型控制,编写代码中不会判断是否可能造成隐式类型转换。效果等同于使用 JavaScript 直接定义变量,存在意义也只是为了兼容性
1 2 3 4 5 6 7 8 9 10 11 12 function sum (a :mixed) { a.substr (1 ) a+"100" if (typeof a == "string" ){ a.substr (1 ) } }function sum (a:any ){ a.substr (1 ) }
flow 运行环境 API 在不同运行环境下,flow 有不同的对应 API,如在浏览器环境下,flow 就有 Element 类型等
TypeScript
安装 通过 npm 进行安装,可以安装到全局环境也可以安装到开发环境。安装到开发环境的好处是安装好依赖可以直接运行
1 2 3 4 npm i typescript -g npm i typeScript -D tsc -V
编译
编译流程
使用 tsc 工具进行编译
先进行语法类型检查
然后移除类型注解
将 ES 新特性转换为 ES5 语法
编译指定文件
编译整个项目
通过根目录下配置文件 tscconfig.json 编译整个项目
生成配置文件 tsc --init
直接执行 tsc 命令进行编译 npm run tsc tsc
tscconfig.json 配置项说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 "target" :"es5" "module" :"commonjs" "outDir" :"./" "rootDir" :"./" "lib" :["ES2015" ,"DOM" ] "moduleResolution" :"node" , "skipLibCheck" :true "sourceMap" :true "strict" :true
中文错误提示 1 npm run tsc --locale zh-CN
类型 所有的类型注解,在运行阶段约束都将没有意义,编译后都将成为原始 JavaScript 代码
原始类型 1 2 3 4 5 6 7 8 9 let a :string = "foor" let b :number = 100 | NaN | Infinity let c :boolean = true | false let d :void = null | undefined let e :null = null let f :undefined = undefined
联合类型 用于设置变量可以接收多种类型中某种类型数据
1 let a :string | number = "foo"
标准库 说明
标准库就是 JavaScript 或 运行环境 内置对象所对应的声明,如果没有配置相应的标准库,TypeScript 就无法识别新的内置对象类型。 如:Promise 是 ES6 新的内置对象,TypeScript 默认使用的是 ES5 和 其他标准库,所以在进行类型检测时并不理解 Promise 这种类型,因此会报错。
解决
设置 "target":"es5" 为 "target":"ES2015",即编译后的 js 使用 es6 标准,此时就可识别新的内置对象类型 Promise 等
配置标准库
"target":"es5" 保持不变,依旧编译为 es5 标准的 js 文件
配置 "lib":["ES2015","DOM"] 配置项,添加 es6标准库 和 浏览器环境标准库
配置了 lib,类型检查就只会在此配置项中配置的标准库去查找
object 类型 object 类型指非原始类型,对象、数组、函数
1 2 let foo :object = function ( ){...} let a :{for :string,bar :number} = {for :"aa" ,bar :12 }
数组类型 1 2 let a :Array <number> = [1 ,2 ,3 ] let b :number[] = [1 ,2 ,3 ]
元组类型 什么是元组
明确数组元素数量、明确数组元素类型的注解类型
1 2 3 let a :[number,string] = [18 ,"xx" ] a = [18 ,"xx" ,15 ] a = [18 ,19 ]
枚举类型 什么是枚举
规定变量接收的值只能在一个数据范围中进行获取
特点
可以不指定枚举的值,不指定的话枚举的值从0开始累加
只指定第一个值为某数字,则后面没指定的值在此基础上累加
枚举的值为字符串,则枚举所有值都要设置初始值
枚举在编译之后不会被移除,而是编译为一个对象(双向键值对对象),使得可以用索引值获取枚举名称。p[0]
不通过索引获取枚举名,并且希望编译后移除枚举对象,则应该使用 const const enum p {...} 定义枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 enum p { a = 0 , b = 1 , c = 2 } enum q { a, b, c } enum r { a = 6 , b, c }let obj = { name :"xx" , status :p.a }
函数类型 函数声明
(a:number,b:string=10,c?:boolean,...rest:number[])=>string 此整体表示一个函数类型
形参 添加 类型注解 ,规定参数可以接收的数据类型,实参的 参数类型 和 参数个数 必须保持一致
( ) 后添加类型注解,规定函数的 返回值类型
参数设置为 可选参数 ,形参后面加 ? ,可选参数放在最后
默认值参数 在形参后用 = 直接赋值,默认值参数放在最后
接收 任意数量参传 ,使用es6 ... 操作符,可以给任意数量参数添加数组类型注解,因为 任意数量参数 是个数组
函数第一个参数定义为 this:void,此参数表示 this,他是个伪参数,编译后不会有此形参
1 2 3 function fn (a :number,b :string=10 ,c?:boolean,...rest :number[]):string{ return "foo" }
函数表达式
作为函数调用
函数表达式 值的部分 与函数声明相关的类型注解相同
函数表达式 变量部分 可以添加类型注解
作为回调函数
变量类型必须是 函数类型 ,并且可注解形参和返回值类型,规定调用时接收实参的类型
1 2 3 4 5 let fn :(a:number,b:string ) => string = function (a:number,b:number ):string { return "foo" }
任意类型 效果等同于 JavaScript 变量赋值,可以接收任意类型的数据,且在运行过程中可以接收其他类型值 作用是为了兼容性,不建议使用
1 2 let a :any = 12 a = "foo"
隐式类型推断
如果变量没有定义类型注解,TypeScript 会根据代码推断其类型
被推断的了类型变量将不可以再接收其他类型值
如果无法推断类型,则将标记为 any
1 2 3 4 5 6 let a = 18 a = "foo" let b b = 123 b = "foo"
类型断言
TypeScript 无法推断变量的类型,将其定义为 any 类型,如果后续代码相关操作可能会造成隐式类型转换,则会 TypeScript 会报错
为了解决这个问题,需要 明确告诉 TypeScript 以某种类型进行操作
1 2 3 4 5 6 let a = [100 ,190 ,232 ,180 ]let b = a.find (i => i>0 ) b * b let c1 = b as number let c2 = <number>b
接口
说明和基础用法
是一种规范,用来约束对象的结构
属性名
属性类型
其他的类型都是约束基本类型的数据,接口是约束对象的
和其他类型注解一样,在运行阶段约束都将没有意义
1 2 3 4 5 6 7 8 9 10 interface p { title :string age :number }function fn (post :p) { console .log (p.title ) console .log (p.age ) console .log (p.name ) }
类型扩展
可选成员:title?:string
只读成员:title:undefined
动态成员:[foo:string]:string
1 2 3 4 5 6 7 8 9 10 interface p { title?:string readonly name :string }let a :p = { title :undefined , name :"aaa" } a.name = "bbb"
1 2 3 4 5 6 7 8 9 10 11 12 13 interface p { [foo :string]:string }let a :p = {} a.foo = "aaa" a[2 ] = "bbb" a.stt = 123
类
基本使用 概念
生活中:描述一类具体事物的抽象特征
代码中:描述一类具体对象的抽象成员(属性)
说明
ES6 之前使用 函数 + 原型 实现类
ES6 有 clss 语法糖实现类
TypeScript 增强 class 相关语法
1 2 3 4 5 6 7 8 9 10 11 12 13 class P { name :string = "foo" age :number constructor (name :string,age :number) { this .name = name this .age = age } fn (msg :string):void { console .log ( `I am ${this .name} , ${msg} ` ) } }
类成员的访问修饰符 说明
成员:属性、方法
用于控制类中成员的可访问范围
访问修饰符
公有成员 public:允许在任何地方访问成员【类中通过 this 调用 或 类外通过对象调用】。成员不添加任何修饰符,默认就是 public。
私有成员 private:只允许在同一个类中访问【类中通过 this 调用】
受保护的成员 protected:允许在同一个类和子类中访问【类中通过 this 调用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class P { public name :string private age :number protected msg :string constructor (name :string,age :number,msg :string) { this .name = name this .age = age this .msg = msg } fn (msg :string):void { console .log ( this .age ) } }let pp = new P () pp.name class q extends p { constructor (name :string,age :number,msg :string) { this .super (name,age,msg) console .log (this .msg ) } }
特例
如果构造函数被定义为私有成员,则需要在类中声明一个静态函数,然后在静态函数中调用构造函数并返回实例对象(调用构造函数就是使用 new)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class P { public name :string private age :number protected msg :string private constructor (name :string,age :number,msg :string) { this .name = name this .age = age this .msg = msg } static fn () { return new P () } }let pp = P.fn ()
只读属性
定义属性是只读的,可以初始化属性值,但是不允许对初始化的属性值进行修改
只读标识符 readonly 必须放在属性访问修饰符之后
1 2 3 4 5 6 7 8 9 10 11 class P { public name :string private readonly age :number = 12 protected msg :string private constructor (name :string,age :number,msg :string) { this .name = name this .age = age this .msg = msg } }
类和接口
接口约束了类实现对象的结构
从现实中理解是抽离出公有特性进行约定
只是进行约定,没有具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 interface Name _I { name (fristname :string):void } interface Age _I { age (year :number):void }class P implements Name _I,Age _I{ name (fristname :string):void { return fristname } age (year :number):void { return year } }class Q implements Name _I,Age _I{ name (fristname :string):void { return fristname } age (year :number):void { return year } }
抽象类
抽象类可以包含属性或方法的实现,但是接口只能约定不能实现
抽象类中可以定义抽象方法或抽象属性,必须要子类进行实现。效果与接口相同
类只能继承一个抽象类,但是可以实现多个接口
抽象类在运行时存在,可以用 instanceof 检查,接口只在编译时存在
1 2 3 4 5 6 7 8 9 10 11 12 abstract clss Name { name (fristname :string):void { return fristname } abstract age (year :number):void }class P extends Name { age (year :number):void { return year } }
泛型
函数、类、接 口中的类型变量,以便在调用时指定具体的类型
调用时通过 < > 给类型变量赋指定类型
1 2 3 4 5 function p <T> (length :number,value :T):T[]{ let arr = Array <T>(length).fill (value) return arr }let res = p<string>(3 ,"foo" )
类型声明 当使用第三方模块,但不是使用 TypeScript 编写,无法在 TypeScript 中进行类型检查,因此需要进行类型声明
使用 declare 对属性进行类型声明
下载对应的类型声明模块
有的模块自带类型声明文件,不需要其他操作
1 2 import { camelCase } from 'lodash' declare function camelCase (input :string):string