28Vue-TS(三)

83 阅读7分钟

类型断言as

图片.png

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的检查,不过在运行阶段还是会报错的。代码还是不严谨的。

图片.png

符号: ?. 表示可能有值,也可以能没有

当对象的属性不存在的时候,会短路,直接返回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

图片.png

??:当左侧是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)

更推荐第一种方法,各取所需

类型缩小

图片.png

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)

instanceofin的关键区别就在于: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 { }