大家好,欢迎和我一起来到 Typescript 的世界,开启一个新世界的大门,这一章主要来由浅入深的学习 Typescript。
大家都知道我们要学习的 Vue3 现在完全是使用 Typescript 进行重写的,不仅仅是 Vue,还有很多耳熟能详的大项目都使用了 Typescript,比如说 Angular 这个 google 出品的大而全的框架,ant-design 阿里出品的 React 组件库,Nativescript,Mobx,Nestjs 等等都完全使用 Typescript 进行开发。首先我们先介绍一下本章的脉络。
什么是 Typescript
动态类型语言(运行时才会检查数据类型):js,python
静态类型语言(书写时就会检查数据类型):c,c++,java,ts
为什么使用TypeScript
- 程序更容易理解
- 效率更高
- 更少的错误
程序更容易理解(解决了js的两个问题:函数或者方法输入输出的函数类型,外部条件的等不清楚
动态语言本身的约束:需要运行过程中手动调试,效率高,在不同的代码块和定义中进行跳转,代码自动补全
,更少的错误,编译期间能够发现大部分错误,杜绝一些比较常见的错误,非常好的包容性,完全兼容js,第三方库可以单独编写类型文件
缺点:增加了学习成本,短期内增加了开发成本
全局安装TypeScript
npm install -g typescript
使用 tsc 全局命令:
// 查看 tsc 版本
tsc -v
// 编译 ts 文件
tsc fileName.ts
数据类型及定义
7种原始数据类型:Boolean,Number,Null,Undefined,Number,Bigint,Symbol(es6)
引用类型:boject
any可以更改任何数据类型,是万金油类型
总结:
- Boolean
- Null
- Undefined
- Number
- String
特殊的 any 类型
Array 数组类型
Tuple 元祖
- 元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用
let a:string = 'tyc'
let b:boolean = false
let c:number = 3
let d:number = null//null和undefined是所有数据类型的子类型
let e:any = '3'//any可以更改任何数据类型
//数组
let array:number[] = [2,1,3]//声明数组的类型只能为数字
//元祖类型
let user :[string,number] = ['你好',2]//类型不能错,源和目标数量要一致
user.push(2,2)//push可以不断进行不断添加,但是要在类型范围内
Interface接口
对对象的形状进行描述
Duck Typing(鸭子类型)
interface Person{
readonly id :number,//只读属性
name:string,
age:number,
salary?:number //?可选可不选
}
let viking:Person = {
id:956,
name :'tc',
age:24,
}
viking.id = 12//报错.不允许修改
Function函数
限定函数的输入和输出
function add(x:number,y:number,z?:number){
if(typeof z === 'number'){
return x+y+z
}else{
return x+y
}
}
//ts中:后面的对象都是在声明数据类型
add(1,2,2)
interface Isum{
(a:number,b:number,c:number):number
}
//let add2:(a:number,b:number,c:number)=>number=add
let add2:Isum =add
add2(2,3,4)
类型推论 - 没有明确的指定类型的时候推测出一个类型
联合类型 - 表示类型或的关系
类型断言 - TypeScript 类型断言用来告诉编译器你比它更了解这个类型
- 不是类型转换,断言成一个联合类型中不存在的类型是会出现错误的
类型守卫 - 当遇到一个联合类型的时候,使用条件语句,它可以自动帮你来缩小类型的范围
- typeof 和 instanceof 关键字
//type inference
let str = 'str'
str =123//报错
//union types 联合类型
let numberOrString:number | string
numberOrString.toString //访问两者共有的方法
function getLength(input:string|number):number{
const str = input as string
if(str.length){
return str.length
}else{
const number = input as number
return number.toString().length
}
}
//type guard 遇到联合类型可以缩小类型范围
function getLength2(input:string|number):number{
if(typeof input == 'string'){
return input.length
}else{
return input.toString().length
}
}
枚举(enums)
枚举值分为两种 const常量值和计算值computed
以下都是常量值
enum Direction {
up,
down,
left,
right
}
console.log(Direction.up)//0 返回的下标
console.log(Direction.down)//1
console.log(Direction[0])//up
也可以给枚举项手动赋值
enum Direction {
up=10,//此时以下的枚举项就会进行递增
down,
left=6,
right//此时right的枚举项就是7
常量枚举
const enum Direction {
up ='up',
down ='down',
left ='left',
right = 'right'
}
const value = 'up'
//假如value是res得到的数据
if(value == Direction.up ){
console.log('goUp')
}
泛型(Generics)
泛型:定义时不指定类型,使用时才指定类型
泛型支持多个联用
function echo<T>(arg:T):T{ //传入什么值就返回什么值
return arg
}
const result = echo(2)
function swap<T,U>(tuple:[T,U]):[U,T]{
return [tuple[1],tuple[0]]
}
const result2 = swap(['1','冯昕'])
泛型约束
通过 extends 关键字来设置约束条件,而不是想传入啥就传入啥
泛型和数组联用:T[]
泛型和接口联用: 使用extends关键字继承接口
interface IWithLenght{
length:number
}
function echoWithArrLenght<T extends IWithLenght>(arg:T):T{
console.log(arg.length)
return arg
}
const str = echoWithArrLenght('123')
const obj = echoWithArrLenght({length:2,width:10})
let arr2 = echoWithArrLenght([1,2,3])
泛型在类和接口的使用
class Queue<T>{
private data = [];
push(item:T){
return this.data.push(item)
}
pop():T{
return this.data.shift()
}
}
const queue =new Queue<number>()
queue.push(1)
queue.push('str')//不能将string类型的值赋给number
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed())
interface KeyPari<T,U>{
key:T,
value:U
}
let kp1:KeyPari<number,string> = {key:250,value:'冯昕'}
let kpl2:KeyPari<string,string[]> = {key:'对象',value:['钟无艳','张飞']}//U:声明数组里面的元素是字符串
泛型总结
一、创建一个拥有特定类型的容器,比如:class 和interface上的泛型仿佛给你一个容器贴标签一样,比如:
let arrTwo:Array<number>=[1,2,3]
给这个数组贴了一个number的标签,告诉arrTwo,是一个装满number类型的数组或者是装着数字的队列
二、灵活约束参数的类型,传入的参数必须是xx方法xx属性,否则就会报错
三、函数的使用,函数的类型推断不会流入到函数体内,所以使用表达式没法建立类型的绑定,用泛型可以打破这个鸿沟
类型别名(type aliase),字面量,交叉类型
用type重新定义各种类型的别名,类似快捷方式,交叉或者组合的时候考虑使用类型别名
let sum = function(x:number,y:number):number{
return x+y
}
const result = sum(1,2)
//创建类型别名,相当于快捷方式
type PlusType =(x:number,y:number) =>number
let sum2 :PlusType //赋值类型
const result2 =sum2(2,3)//这里只声明了类型,没有实现,在js中运行会报错
//创建联合类型别名
type StringOrNumber = string | number
let result3 : StringOrNumber
result3 = 1 //如果类型不是sring和number会报错
//字面量
const str:'冯昕' = '冯昕' //这里会报不能将类型冯2昕分配给类型'冯昕'
const number :1 = 1
type Directions ='UP' | 'Down' | 'left' | 'right'
let toWhere : Directions = 'UP'
// 交叉类型
interface Iname {
name:string
}
type Iperson = Iname & {age:number}
let person:Iperson = {name:'tc',age:24}
内置类型
partial 可以快速把某个接口类型中定义的属性变成可选的
Omit 让可以从另一个对象类型中剔除某些属性,并创建一个新的对象类
//global objects
const a :Array<number> = [1,2,3]//按住ctrl查看定义文件
const date = new Date()
date.getTime()
const reg = /abc/
reg.test('abc')
//build-in object
Math.pow(2,2)
// DOM and BOM
let body = document.body
let allLis = document.querySelectorAll('li')
allLis.keys()
document.addEventListener('click',()=>{
console.log('tc')
})
// Utility Types
interface Iperson{
name:string,
age:number
}
let viking:Iperson = {name:"tc",age:22}
// Partial 类型为值为可选的,可传可不穿
type Ipartial = Partial<Iperson>//Iperson当做泛型传进去
let viking2:Ipartial = {name:'tc'}
// Omit忽略某个属性,忽略name,只剩age
type Iomit = Omit<Iperson,'name'>
let viking3:Iomit ={age:22}