什么是TypeScript
- TypeScript简称 TS,可以理解为是 JavaScript 的扩展
- TypeScript 又叫做静态的 JavaScript,不能直接引入到 html 中,不能直接被浏览器识别,需要经过 ts 转换器或者是 babel 转化后才能使用
- TypeScript 是添加了类型系统的 JavaScript,适用于任何规模的项目。
1.TypeScript相比JS的优势
JS的类型系统存在“先天缺陷”,绝大部分错误都是类型错误( Uncaught TypeError )。●优势一:类型化思维方式,使得开发更加严谨,提前发现错误,减少改Bug 时间。
优势二:类型系统提高了代码可读性,并使维护和重构代码更加容易。
优势三:补充了接口、枚举等开发大型应用时JS缺失的功能。
2. 关于 TypeScript
- TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
- 它的第一个版本发布于 2012 年 10 月,经历了多次更新后,现在已成为前端社区中不可忽视的力量,不仅在 Microsoft 内部得到广泛运用,而且 Google 开发的 Angular 从 2.0 开始就使用了 TypeScript 作为开发语言,Vue 3.0 也使用 TypeScript 进行了重构。
- 类型系统按照类型检查的时机来分类,可以分为动态类型和静态类型
- 动态类型是指在运行时才会进行类型检查,这种语言的类型错误往往会导致运行时错误。JavaScript 是一门解释型语言,没有编译阶段,所以它是动态类型;
- 静态类型是指编译阶段就能确定每个变量的类型,这种语言的类型错误往往会导致语法错误。TypeScript 在运行前需要先编译为 JavaScript,而在编译阶段就会进行类型检查,所以 TypeScript 是静态类型
- 类型系统按照「是否允许隐式类型转换」来分类,可以分为强类型和弱类型
- TypeScript 是完全兼容 JavaScript 的,它不会修改 JavaScript 运行时的特性,所以它们都是弱类型。
TS安装编译
npm install -g typescript
以上命令会在全局环境下安装 tsc
命令,安装完成之后,我们就可以在任何地方执行 tsc
命令了
检测是否安装成功命令:tsc -v --------Version 版本号 证明安装成功
编译
- 创建一个ts文件
- 进入命令行
- 进入ts文件所在的目录
- 执行以下命令
tsc ts文件
- 自动编译配置
- 首先在放 ts 的文件夹下打开终端 执行 tsc --init 命令 会生成一个 tsconfig.json 文件
tsc --init // 在文件夹下自动生成一个 tsconfig.json 文件
-
点击 vscode 终端–>选择运行任务–>输入 tsc 按下回车–>选择 tsc:监视 xxxx 文件夹下的 tsconfig.json 文件
-
之后再写的 ts 代码就会自动转化为 js
TS数据类型
- 布尔类型
let isDone: boolean = false; // 编译通过 // 后面约定,未强调编译错误的代码片段,默认为编译通过
- 数字类型
let isDone: number = 1 - 字符串类型
let isDone: string =‘hope’ - void类型
JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用void
表示没有任何返回值的函数 - undefined 和null 类型
在 TypeScript 中,可以使用null
和undefined
来定义这两个原始数据类型let u: undefined = undefined let n: null = null;
undefined
和null
是所有类型的子类型。也就是说undefined
类型的变量,可以赋值给number
类型的变量 - never其他类型
never
类型表示的是那些永不存在的值的类型。 例如,never
类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是never
类型,当它们被永不为真的类型保护所约束时。never
类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never
的子类型或可以赋值给never
类型(除了never
本身之外)。 即使any
也不可以赋值给never
声明数组的方法
- 类型 + 方括号 表示法
例如数字型…
let numArr:number[] = [1,2,3,4] // 声明一个数组,数组的元素只能是数字类型
numArr.push(10) // 正常追加
- 数组泛型
// 使用泛型声明数组:
let numArr:Array<number> = [1,2] // 声明一个数组,只能包含数字类型的元素
let numArr:Array<string> = ['a','b'] // 声明一个数组,只能包含字符串类型的元素
元组
数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。
- 定义元祖
定义一对值分别为string
和number
的元组:
let tom: [string, number] = ['Tom', 25];
当赋值或访问一个已知索引的元素时,可以只赋值其中一项
let tom: [string, number];
tom[0] = 'Tom';
直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。
// 正确的写法
let tom: [string, number];
tom = ['Tom', 25];
类型推论
如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型
let str = 'seven'; //这时就会被会被推断成string类型
str = 7; 赋值会报错
推论any类型
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:
let str;//这时就会被会被推断成any类型
str = 'seven';
str = 7;不会报错
联合类型
联合类型表示取值可以为多种类型中的一种。
- 语法
let numStr: string | number;
numStr = 'seven';
numStr = 7; //联合类型使用 `|` 分隔每个类型。
- 访问联合类型的属性或方法
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
访问string
和number
的共有属性
function getString(something: string | number): string {
return something.toString();
}
接口
-
接口介绍
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
-
接口的描述
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
接口的用法
- 定义接口
我们可以使用 interface来定义接口;例如:
- 定义接口
// 定义一个接口
interface IUser {
name:string,
age:number
}
定义接口注意事项:
- 定义接口 语法 interface接口名 {}
- 接口名称尽量以大写的 I 开头
- 接口中的属性之间可以写分号 、逗号或者不写
接口描述对象
- 使用接口描述对象的形状
我们首先定义一个名称为IUser的 接口,接口中有两个属性name和age;要求name属性类型为string,age属性类型为number;代码如下:
// 定义一个名称为 IUser 的接口;
interface IUser {
name: string
age: number
}
let tom:IUser = {
name: 'tom', //增加变量和减少变量是不允许的,改变属性的数据类型是不允许的;
age: 18 //赋值的时候,变量的形状必须和接口的形状保持一致。
}
// IUser接口约束tom对象完成
接口属性
接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。我们希望不要完全匹配一个形状,那么可以用可选属性;
可选属性的定义,在定义接口的时候,属性和类型的冒号前加上英文的问号(?),
interface IUser {
name: string
age: number
gender?: string // 定义可选属性
}
let tom:IUser = {
name: 'tom', //使用接口约束对象
age: 18 //tom对象可以有 gender属性也可以没有 gender属性;
}
任意属性
有时候我们需要给对象添加任意的属性,那么就需要接口有任意的属性;
就可以使用 [propName: string] 定义任意属性,属性值取 string 类型的值。
interface IUser {
name: string
age: number
gender?: string,
[prop:string]:string|number // 添加任意属性,一旦定义了任意属性,
那么上面的确定属性和可选属性的类型都必须是它的类型的子集,
可以理解为之添加了任意属性,那么确定属性和可选属性都是他的子集。
}
只读属性
有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性;
// 定义接口
interface IUser {
readonly id: string, // id为只读属性
name: string
age: number
gender?: string,
[prop:string]:string|number
}
使用 IUser 接口 约束对象:
let tom:IUser = {
id: '123',
name: 'tom',
age: 18,
email: 'jiyun@163.com'
}
接下来,我们改变 tom 的 id 值
tom.id = '456'
// 报错: 无法分配到 "id" ,因为它是只读属性。
注意: 只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
使用接口限制ajax案例
// 实例化ajax核心方法
let ajax = new XMLHttpRequest()
// 建立连接 open('请求方式','请求连接',同步或者异步)
ajax.open('get','https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata')
// 发送请求
ajax.send()
// ajax状态判断
ajax.onreadystatechange = function(){
if(ajax.status==200 && ajax.readyState==4){
console.log(ajax.response);
}
}
// 将 请求方法、请求连接和请求参数封装到函数参数内
function request(params){
let ajax = new XMLHttpRequest()
ajax.open(params.method,params.url)
ajax.send(params.data)
ajax.onreadystatechange = function(){
if(ajax.status==200 && ajax.readyState==4){
console.log(ajax.response);
}
}
}
使用TS约束上述函数中的参数
定义接口约束:其中请求方式为必传属性,请求连接为必传属性,请求参数为可选属性;
// 定义一个接口,
interface IParams {
method: string
url: string
data?: any
}
接口约束:
function request(params:IParams){
let ajax = new XMLHttpRequest()
ajax.open(params.method,params.url)
ajax.send(params.data)
ajax.onreadystatechange = function(){
if(ajax.status==200 && ajax.readyState==4){
console.log(ajax.response);
}
}
}
调用接口
request()
// 报错:应有 1 个参数,但获得 0 个
request({})
// 报错: 类型“{}”缺少类型“IParams”中的以下属性: method, url
request({method: 'get',url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata'})
// 发送了请求
可索引接口
- 描述数组
// 定义一个接口
interface Iarr {
[prop:number]: string
}
// 使用接口约束数组
let arr:Iarr = ['1','2']
- 描述对象
interface Iobj {
[prop:string]: string
}
let arr:Iobj = {
a: '1'
}
类型断言
类型断言可以用来手动指定一个值的类型
1.语法
值 as 类型
或者<类型>值
我们可以用console.dir()打印DOM元素,在属性后边就可以查看元素类型
2.使用
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型中共有的属性或方法
interface Icat {
name: string
run(): void
}
interface Ifish {
name: string
swim():void
}
let animal:Icat|Ifish
console.log(animal.name);
而有时候,我们确实需要在还不确定类型的时候就访问其中一个类型特有的属性或方法,比如:
interface Icat {
name: string
run(): void
}
interface Ifish {
name: string
swim():void
}
let animal:Icat|Ifish
console.log(animal.name);
if(typeof animal.swim=='function'){
//animal.swim会报错。
//此时可以使用类型断言,将 `animal` 断言成 `Ifish`
console.log('animal是一条鱼')
}else{
console.log('animal是一只猫')
}
//类型断言
if(typeof (animal as Ifish).swim=='function'){
console.log('animal是一条鱼')
}else{
console.log('animal是一只猫')
}
//这样就可以解决访问 `animal.swim` 时报错的问题了
使用类型断言的注意事项
- 类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误
- 代码在编译时不会报错,但是在运行的过程中或报错,因此使用类型断言时一定要格外小心,尽量避免断言后调用方法或引用深层属性,以减少不必要的运行时错误。
约束函数
函数是 JavaScript 中的一等公民, 在 JavaScript 中,有两种常见的定义函数的方式——函数声明和函数表达式
function addNum(x,y){ // js中使用函数声明
return x + y
}
//在TS中,一个函数要有输入(参数)和输出(返回值),
我们在ts内要对其输入和输出进行约束;例如:
function addNum(x:number,y:number):number{
return x + y
}
- 对输入(参数)的约束是在每个参数后面写上 :类型
- 对输出(返回值)的约束是在函数名()后写上 :类型
- 注意: 输入多余的(或者少于要求的)参数,是不被允许的
addNum() // 报错: 应有 2 个参数,但获得 0 个。
addNum(1) //报错: 应有 2 个参数,但获得 1 个。
addNum(1,2,3) //报错: 应有 2 个参数,但获得 3 个。
表达式声明约束
var addNum = function(x,y){
return x + y
} //js中使用函数表达式
var addNum = function(x:number,y:number):number{
return x + y //TS中约束表达式
}
- 对输入(参数)的约束是在每个参数后面写上 :类型
- 对输出(返回值)的约束是在函数名()后写上 :类型
- 注意: 输入多余的(或者少于要求的)参数,是不被允许的
表达式声明的约束的注意事项
表达式声明一个函数
var addNum = function(x:number,y:number):number{
return x + y
}
这是可以通过编译的,不过事实上,上面的代码只对等号右侧的
匿名函数进行了类型定义,而等号左边的 addNum,是通过赋值
操作进行类型推论而推断出来的。如果需要我们手动给 addNum
添加类型,则应该是这样:
var addNum:(x:number,y:number)=>number = function(x:number,y:number):number{
return x + y
}
注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>
**在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。**
接口+表达式约束
在表达式声明函数的时候,为了使函数左侧不通过类型推论推论出来,而是直接手动添加类型:
var addNum:(x:number,y:number)=>number = function(x:number,y:number):number{
return x + y
}
这种写法很繁琐;因此我们可以考虑使用接口约束函数
//定义接口
interface Ifun {
(x:number,y:number):number
}
// 接口约束函数
var addNum:Ifun = function(x:number,y:number):number{
return x + y
}
采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变