TypeScript语法细节
联合类型(满足其中一个即可)
- 可以使用多种运算符,从现有的类型中构建新类型
const number|string = 123
- 可以是这些类型中的任何值
- 但是使用的时候需要小心
let virable: number | string = 123function getData(id: number | string) {//若我们想使用length方法,需要使用类型缩小//直接使用会报错// console.log(id.length)if (typeof id === "string") {console.log(id.length)} else {console.log(id);}
}
类型别名
- 在 TypeScript中我们可以给类型起别名
- 主要是方便代码的阅读以及联合类型的复用
type IDtype = number | stringfunction getData(id:IDtype) {//若我们想使用length方法,需要使用类型缩小//直接使用会报错// console.log(id.length)if (typeof id === "string") {console.log(id.length)} else {console.log(id);}
}
接口声明
- 在 TypeScript中可以使用interface,对对象类型进行取别名
//采用声明式命名
interface IPerson {name: string,age:number
}
- 在大部分情况下,type与interface是没有区别的,实现的功能是一样的
type与interface的区别
- 一是type可以对应多种类型,而interface只能针对于对象类型
- 二是type不能对同一别名重复声明,而interface可以
- interface重复声明一个别名,可以将其进行合并
- 三是interface具有继承性,方便了代码后期的拓展
// * 一是type可以对应多种类型,而interface只能针对于对象类型
type numberType = number
type ObjType = { x: string }
interface IObj { x: string }// * 二是type不能对同一别名重复声明,而interface可以
// * interface重复声明一个别名,可以将其进行合并
interface IObj { y: number }//需要满足x以及y// * 三是interface具有继承性,方便了代码后期的拓展
interface IObjZ extends IObj {//需要满足x以及yz:boolean
}
- 总结,在实际开发中,对象类型使用interface,其他类型使用type即可
交叉类型(多种类型要同时满足)
- 通常是针对于对象类型进行的
- 若是普通类型的使用交叉类型,则会自动推断出是never类型
type myType = number & string
//通常不会这样操作
- 现在有两个对象类型,需要同时满足
interface IObj1 {name: stringage:number
}interface IObj2{walk:()=>void
}type IObj1IObj2 = IObj1&IObj2
//类型注解的时候可以使用IObj1IObj2,与下面的代码一致
const myObj: IObj1 & IObj2 = {name: "zhangcheng",age: 18,walk() {console.log("walk");}
}
类型断言as
-
通常用在获取元素节点的时候
-
获取元素节点的时候,若根据标签获取 TS会根据标签推断出生成的是什么类型,可以做后续操作
-
但是若使用 id或者class进行获取,则不会推断出是什么类型,后续的操作也不能正常进行
const imEle = document.querySelector("div")
//可以直接进行对应类型的操作
const imEle = document.querySelector(".myDiv")
//可以进行指定
const imEle = document.querySelector(".myDiv") as HTMLDivElement
- 对于普通类型的也可以进行类型断言
- 一般是由确定的类型转成不太确定的类型
- 或者由宽泛的类型,转成确定的类型
- 注意不能对类型强制类型转换
let str: string = "123" as anylet myAny:any = 123 as number
非空类型断言
- 对标识符后面加!进行非空类型断言
- 在上述的学习中,我们知道,有些标识符是可有可无的
- 对于可有可无的标识符,我们在确保一定存在的情况下,就可以使用非空类型断言
function foo(message?: string) {//直接使用会报错,因为message有可能没有// console.log(message.length);//解决方案一,使用可选链console.log(message?.length)//解决方案二:非空类型断言,在message一定存在的情况下console.log(message!.length)
}
字面量类型
- 当我们使用const声明一个变量的时候,会用后面的值当作一个类型,即为 字面量类型
- 但是单纯的这样写字面量类型,是没有意义的
- 通常用于 类似于枚举的用途
type requestType = "GET" | "POST"function request(url: string, method: requestType) {//这样method只能取GET或者POST
}request("http://xxx","GET")
- 但是若是以下情况,则不能直接传值
- 我们将要传入的信息用对象包裹
type requestType = "GET" | "POST"function request(url: string, method: requestType) {//这样method只能取GET或者POST
}let info = {url: "http://xxxx",method:"GET"
}
//这样直接传会报错,因为info.method是string类型,而函数参数是requestType类型
// request(info.url,info.method)//解决方案一
//使用类型断言
request(info.url, info.method as "GET")//解决方案二
//使用字面量推理
let info2 = {url: "http://xxxx",//url的类型"http://xxxx"method:"GET"//method的类型"GET"
} as constrequest(info2.url,info2.method)
类型缩小
- 目的是让一个类型,缩小到一个更加准确的范围
- 而与类型缩小一同使用的就是 类型保护
- 类型保护的方法通常有以下方法
- typeof
- === !==
- instanceof
- in
//typeof
type messageType = string | number
function getDataId(id: messageType) {//id有可能是string类型或者,number类型if (typeof id === "string") {//判断条件就是类型保护,只有在string类型状态下,才会做如下操作console.log(id.split(" "))}
}//===/!==通常用在判断字面量类型上
type requestType = "GET" | "POST"function request(method: requestType) {//method有可能是GET或者POSTif (method === "GET") {//当method为GET的时候,执行的操作} else {}
}//instanceof
function formate(date: string | Date) {//时间的格式化,用于可能传入string或者Date对象if (date instanceof Date) {//判断date是否是Date实例出来的}
}//in,通常用于判断传入的对象中有没有某个属性
type userInfoType = {name:string,age:number}
let userInfo:userInfoType = { name: "zhangcheng", age: 18 }
function changeUserInfo(userInfo:userInfoType) {//判断userInfo中是否有name属性if ("name" in userInfo) {console.log(userInfo.name);}
}
TypeScript函数的类型
- 在 TypeScript中,函数参数有类型,函数的返回值有类型
- 而函数本身也有类型
- 通过函数类型表达式进行声明
//type fnType = (参数:参数类型)=>返回值类型
type fnType = (num1: number,num2:number) => number//在一个函数中传入另外一个函数
//对这个函数进行类型指定
function numberSum(fn: fnType) {console.log(fn(10,20))
}numberSum(function (num,num1) {return num+num1
})
- 通过函数调用签名的方式进行声明
- 因为函数本身是一个对象
- 若想将函数作为一个对象,且使用其中的属性,就可以使用调用签名的方法
interface Ifn {name: stringage: number//以下就是函数调用签名,代表这个对象可以被调用(num1:number,num2:number):number
}function calc(fn: Ifn) {console.log(fn.name,fn.age);fn(10,20)
}function sum(num1:number,num2:number) {return num1+num2
}
sum.name = "123"
sum.age = 18calc(sum)
- 构造签名
- 是指函数可以通过 new方法进行调用
class Person{}interface ICONPerson {//构造签名new () : Person
}function factory(fn: ICONPerson) {return new fn()
}
参数的可选类型
- 在函数的参数中,有些参数是可以传可以不传的,那么我们可以将这个参数设置为可选参数
- 参数设置为可选参数,其类型就是 指定的类型 | undefined的联合类型
- 其中y就是可选类型
- 一般可选类型都是设置在最后
function sum(x:number,y?:number){}
参数的默认值
- 我们可以给函数的参数设置默认值
- 设置默认值的参数,在实际调用中,可以不用传参
- 设置默认值的参数,可以不用标明类型注解
- 同时该参数可以接收 undefined
function sum(x:number,y=100){}
剩余参数
- 我们可以使用…的方式接收传递进来的剩余参数
function sum(...arr: (string | number)[]) {console.log(arr);
}
函数的重载(了解)
- 现在有一个需求
- 有一个sum函数,接收两个参数
- 这两个参数可能是number类型或者string类型
- 返回两个参数相加的值
- 注意:联合类型是不能使用+运算符的
//函数重载
function sum(num1: number, num2: number)
function sum(num1:string,num2:string)//通用函数,是不能直接被调用的
function sum(num1, num2) {return num1+num2
}console.log(sum(10, 20));
console.log(sum("10", "20"));
//会报错
// console.log(sum(10,"20"));
可推导的this类型
- 目前在vue3以及react的项目中,其实很少再用到this了
- 但是我们还是应该了解以下this的类型
- 在没有特殊配置的情况下,TS中的this类型就是any类型
let obj = {name: "zhangcheng",sport() {console.log(this);}
}function sum() {console.log(this);
}
-
我们可以通过
tsc --init
初始化TS的配置,会生成一个 tsconfig.json的文件 -
若 noImplicitThis配置项为true,将不会允许模糊的this出现
- 但是this会通过上下文进行推导,推导成功的则正常使用(对象obj中的this)
- 没有推导成功的,则编译不通过(sum函数中的this)
-
同时,我们可以给 sum函数指定this
//第一个参数为指定this类型的参数,这是固定位置,且名称不能变
//后续的参数才是参数
function sum(this:{name:string},arg:number) {console.log(this);
}//只能通过call,bind,apply调用
sum.call({name:"zhangcheng"},10)
this相关的内置工具
- ThisParameterType和OmitThisParameter
function sum(this: { name: string }, num: number) { }//提取函数中的this类型
type sumThis = ThisParameterType<typeof sum>//剔除this,其余参数的类型
type argType = OmitThisParameter<typeof sum>
- ThisType工具,主要是将上下文的this绑定到某一个类型身上
- pinia内部实际上用了这个方法,这只是一个简易版本,运行将会是undefined
interface IState{stateName:string
}interface IStore {state: IStategetData:()=>void
}//这里的this类型,将会是IState类型
let store: IStore & ThisType<IState> = {state: { stateName: "lisi" },getData() {//因此可以直接通过this.stateName访问state中的变量console.log(this.stateName);}
}