类型断言as
document.getElementById()返回HTMLElement,但是是span还是div还是h2呢?这里其实我们是无法知道具体的类型信息的。
通过类型断言,得到具体的类型
//类型断言 as 这个时候我们就可以知道是img,就可以使用他的src
const el = document.getElementById("why") as HTMLImageElement
el.src = "url地址"
class Person{
}
class Student extends Person{
studying(){}
}
function sayHello(P:Person){
//2.我想使用stu的方法
p.studying()//这样会报错的
(p as Student).studying()
//3.这样就可以了,先把p断言成学生,然后调用方法
}
const stu = new Student()
//1.我的stu继承了Person,所以这里是可以传的,尽管限制了person类型
sayHello(stu)
类型断言补充(了解)
骗过编译器的眼睛(别乱用)
const message = "Hello World"
const num: number = message//这样肯定是有问题的
const num: number = message as number//还是会错
const num: number = (message as any) as number
//先转any/unknown类型,再转number就可以了
非空类型断言
当我们执行以下代码时,会报错的
//加了 ? 表示参数可以是 undefined | string
function printMsgLength(message?: string){
console.log(message.length)
}
printMsgLength("Hello World")//正确输出
printMsgLength()//不传参数,代码不会报错,但是tsconfig.js配置会告诉你代码执行时会错
为什么会报错?因为代码写的并不严谨,空的怎么能获取length?
解决方案:使用非空断言
function printMsgLength(message?: string){
//加了一个 !,表示确定message传来的时候一定是有值的!
//跳过ts在编译阶段对他的检测
console.log(message!.length)
}
printMsgLength("aaa")
可选链的使用
如果你写的没有值,但是使用非空类型断言,你可以逃过ts的检查,不过在运行阶段还是会报错的。代码还是不严谨的。
符号: ?. 表示可能有值,也可以能没有
当对象的属性不存在的时候,会短路,直接返回undefined,如果存在,才会继续执行
//定义类型别名
type Person = {
name:string,
friend?:{
name:string,
age?:number
},
}
//必须满足Person类型
const info: Person = {
name:"why",
//如果写上friend就必须写name,因为name不是可选的
friend:{
name:"abc"
}
}
console.log(info.name)//没问题
console.log(info.friend.name)//会报错,ts会说 friend可能是一个undefined
//这里通过非空类型断言可以解决
info.friend!.name
但是我们说过,如果你不想自己骗自己
那么就真的要保证friend里面确实是有一个name的
可是一旦代码过多,找到info中的friend是比较麻烦的
所以我们这里更推荐使用可选链,不会报错了
info.friend?.name
//可能有值也可能没有,如果friend不存在,那么短路--直接不执行
整个表达式返回undefined,但是执行不会报错
//如果存在,才会继续执行friend,然后找到里面的name值
?? 和 !! 的使用
!!:将其他类型转换为boolean类型
const message = "Hello World"
//将message转为boolean类型,然后赋值给flag
const flag = Boolean(message)
//如果是一个 ! 就是取反,所以要
const flag = !!message
??:当左侧是null或undefined时,返回右侧操作数,否则返回左侧(有值的情况下)
let message: string|null = null
const content = message ?? "你好啊"
console.log(content)
//默认肯定是输出null,但是我们使用??,当message为null时,
他会输出成后面的值,即输出:你好啊
message ? message : "你好啊"
我们发现他和||很像,但是||专门用在if判断中,除了if判断的地方,其他地方我们都是推荐使用??
字面量类型
"Hello World"也可以作为类型,叫做字面量类型
const message:"Hello World" = "Hello World"
//123也作为了类型
const num:123 = 123
//num = 321 不可以的,字面量类型要和赋值是一样的
字面量类型的意义:必须结合联合类型
//这样align的赋值可以是这三个中的其中一个类型
let align: 'left' | 'right' | 'center' = 'left'
字面量推理
type Method = 'GET'|'POST'
function request(url:string,method:Method){}
const options = {
url:"http...",
method:"POST"
}
//url可以传,但是后面的method不可以
request(option.url,options.method)
//因为options对象没有类型限定,ts会对他进行类型推导,推导出url:string,method:string
//但是我request中method要求传入一个GET或POST
//所以这里存在设计缺陷
解决方法
type Method = 'GET'|'POST'
function request(url:string,method:Method){}
type Request = {
url:string,
method:Method
}
---------------------------------------
第一种方法:
//限定是Method类型,即GET或POST
const options:Request = {
url:"http...",
method:"POST"
}
//这样就可以了
request(option.url,options.method)
---------------------------------------
第二种方法:
const options = {
url:"http...",
method:"POST"
}
//类型断言:告诉ts,我这肯定是Method类型的
request(option.url,options.method as Method)
---------------------------------------
第三种方法:
const options = {
url:"http...",
method:"POST"
} as const
//通过as const 转换为具体字面量类型
request(option.url,options.method)
更推荐第一种方法,各取所需
类型缩小
1.typeof的类型缩小
type printID = number | string
function printID(id: printID){
if(typeof id === 'string'){
//本来是联合类型,我缩小成string,类型缩小
//是string,我就小写字母,类型保护,因为只有string可以字母小写
console.log(id.toUpperCase())
}else {
console.log(id)
}
}
2.平等的类型缩小
=== == !== != switch
type Direction = 'left'|'right'|'top'|'bottom'
function printDirection(direction:Direction){
if(direction === 'left'){
console.log(direction)
} else if(...)
switch(direction){
case 'left':
console.log(direction)
break;
case 'top':
console.log(direction)
break;
}
}
3.instanceof
function printTime(time: string|Date){
//不允许传来的是一个string,结果调用Date中方法
//类型缩小,判断time是不是Date类型
if(time instanceof Date){
console.log(time.toUTCString())
}else {
console.log(time)
}
}
------------------------------------------
class Student{
studying(){}
}
class Teacher{
teaching(){}
}
const stu = new Student()
function work(p:Student | Teacher){
if(p instanceof Student){
p.studying();
} else{
p.teaching();
}
}
work(stu)
4.in
//指定函数类型 ()=>void,没有参数没有返回值
type Fish = {
swimming: ()=>void
}
type Dog = {
running: ()=>void
}
function walk(animal: Fish|Dog){
//判断swimming有没有在我们的字面量里,这里amimal就是传来的fish
//如果不存在的话,我是调用不了的,所以要先判断一下
if('swimming' in animal){
animal.swimming()
} else{
animal.running()
}
}
const fish: Fish = {
swimming(){
console.log("swimming")
}
}
walk(fish)
instanceof和in的关键区别就在于:instanceof需要创建一个实例,将实例传进来并判断类型,而in不需要,他是一个字面量,判断属性在不在字面量里面。
TypeScript函数类型
函数可以作为参数,也可以作为返回值进行传递
function foo(){
}
//书写函数类型:当前fn是一个函数类型,无参 并 无返回值
function bar(fn: () => void){
fn()
}
bar(foo)//将foo函数作为参数传递给bar函数
//定义常量时,编写函数的类型
//add是一个函数类型并且有形参
const add:(num1: number,num2: number)=>number/void = (num1: number,num2: number) => {
return num1 + num2
}
//这里就算限定函数类型返回void也是不会报错的
//因为void是一个宽泛类型,你可以返回string、number、null、undefined都可以
//但是建议最好严格一些
函数类型的案例
//三个参数n1、n2是number,fn是函数类型
function calc(n1:number,n2:number,fn:((num1:number,num2:number)=>number){
return fn(n1,n2)
}
calc(20,30,function(a1,a2){
return a1 + a2;
})
calc(20,30,function(a1,a2){
return a1 * a2;
})
函数的参数可选类型
//可选类型是必须写在必选类型的后面的
//y相当于是 undefined | number 的联合类型
function foo(x: number,y?: number){
}
foo(20,30)
foo(20)
function foo(x: number = 20,y: number){
console.log(x,y)
}
foo(undefined,30)//x写undefined就可以使用默认值20
如果想直接不传,就需要把默认的放在后面
function foo(x: number,y: number = 20){
console.log(x,y)
}
foo(20)//表示x是20,对y不传值,默认就是20
书写规范:必传参数---有默认值的参数---可选参数
函数的剩余参数
剩余参数都是放在函数声明的时候所有的参数的 最后面
//可以传任意多个number类型的参数
function sum(initalNum: number, ...nums: number[]) {
let total = initalNum
for (const num of nums) {
total += num
}
return total
}
console.log(1,2,3,4,5,6,7)//1 ,数组里存放2,3,4,5,6,7
console.log(sum(20, 30))
console.log(sum(20, 30, 40))
console.log(sum(20, 30, 40, 50))
this的问题
// 在TS中 this 是不能乱用的
// 独立函数 推导不出来 this,因为this在独立函数中指向window
// 解决方案:传一个参数,并且放在第一位
function eating(this: { name: string }) {
console.log(this.name + " eating");
}
const info = {
name: "呆呆狗",
eating: eating,
}
// 隐式绑定
info.eating()
// 显示绑定
// 如果 传递了 this ,就不能直接写 eating()
eating.call({ name: "2222" })
export { }