攀爬TS之路(五) 类型断言

简介: 攀爬TS之路(五) 类型断言

类型断言

第二段路时,已经提到联合类型:变量只能访问联合类型中所有类型共有的属性或方法

语法:值 as 类型<类型>值

用途

将联合类型断言成其中的具体类型

interface IFishman {
    // 摸鱼人
    name: string,
    play(): void
}


interface IWorker {
    // 干饭人
    name: string
    eat(): void
}


function myFunc(person: IFishman | IWorker) {
    console.log(person.name)

    person.play()       // 报错:类型“IFishman | IWorker”上不存在属性“play”。类型“IWorker”上不存在属性“play”。
}

但是,有时候我们就是需要访问非公有的属性或方法。比如上面的例子中,当是Fishman时,调用play方法,当是Worker时,调用eat方法。

这时候,断言就能用来将联合类型断言成其中的具体类型

interface IFishman {
    // 摸鱼人
    name: string,
    play(): void
}


interface IWorker {
    // 干饭人
    name: string,
    eat(): void
}


function myFunc(person: IFishman | IWorker) {
    (person as IFishman).play()       // 将person断言成IFishman
}


const fishman: IFishman = {
    name: 'clz',
    play: function () {
        console.log('摸鱼')
    }
}

myFunc(fishman)     // 摸鱼

乍一看,挺好的,但是,实际上只是隐藏其他情况而已,比如上面person as IFishman隐藏了personIWorker的情况,这时候如果传入的参数是IWorker类型的,那就会报错,而且没法在编译阶段就暴露错误

const worker: IWorker = {
    name: 'clz',
    eat: function () {
        console.log('干饭')
    }
}

myFunc(worker)

所以,使用断言时,应该非常注意,不然会增加一些运行时错误

将父类断言成更具体的子类

更准确来说,是将父类型断言成更具体的子类型,因为类的话,使用instanceof来判断就足够了。

但是,如果我们使用接口的话,它并不是类,而是类型,自然就不能使用instanceof来判断,这时候就需要使用断言来将父类型断言成更具体的子类型(实际上和将联合类型断言成其中的具体类型很像)

class Person {
    name: string
}

interface IFishman extends Person {
    // 摸鱼人
    play(): void
}

interface IWorker extends Person {
    // 干饭人
    eat(): void
}

function myFunc(person: Person) {
    // console.log(person instanceof IFishman)      // 会报错:“IFishman”仅表示类型,但在此处却作为值使用。

    if (typeof (person as IFishman).play === 'function') {
        return '摸鱼人'
    } else if (typeof (person as IWorker).eat === 'function') {
        return '干饭人'
    }

}

const worker: IWorker = {
    name: 'clz',
    eat: function () { }
}
console.log(myFunc(worker))     // 干饭人

将任何一个类型断言成any

我们使用JS进行开发时,有时候可以在window对象上添加新的属性,这个属性就能够全局访问了,但是,在TS中是会报错的,因为window对象没有该属性,就会报错。

但是,这个做法实际上在开发中能够很便利,这个时候可以使用断言将它断言成any类型,这样子就能够添加新属性了。

(window as any).a = 123

需要注意的是,这样可能会掩盖真正的类型错误

any断言成具体类型

设想一个情境,一个获取两个参数的和的函数,返回值按理应该是number类型,但是,结果却是any类型,这样子就会导致很多能在编译阶段暴露出的错误没法暴露出来。(这个情境是随便想的,简单来讲就是,历史代码不太好动,可能会引发蝴蝶效应)

function mySum(a: number, b: number): any {
    return a + b
}

const sum = mySum(9, 8)
console.log(sum.length)

比如上面,我们应该在访问sum.length时报错才对,但是因为是任意类型,所以不会报错,所以这时候就可能使用断言,将any断言成具体的类型,恢复它的在编译阶段报错的功能。

断言规则

如果A兼容B或者B兼容A,那么A能够被断言为B
这里的兼容简单来说就是:A兼容B就是指类型A是类型B的子集。

class Person {
    name: string
}

interface IFishman extends Person {
    // 摸鱼人
    play(): void
}

function testPerson(person: Person) {
    return (person as IFishman)
}

function testIFishman(fishman: IFishman) {
    return (fishman as Person)
}

上面的例子中,类型Person是类型IFishman的子集,即Person兼容IFishman,所以Person能被断言为IFishmanIFishman也能被断言为Person

上面使用的Person是类,而IFishman是继承了Person,所以可能会误以为是继承关系导致的能否被断言。实际上,断言并不是根据是否有继承关系,而是看有没有兼容关系。所以下面的做法也是可以的。

interface IPerson {
    name: string
}

interface IFishman {
    // 摸鱼人
    name: string,
    play(): void
}

function testPerson(person: IPerson) {
    return (person as IFishman)
}

function testIFishman(fishman: IFishman) {
    return (fishman as IPerson)
}

如果类型A不兼容B,并且类型B不兼容A,那么A不能断言为B,B也不能断言为A。
比如numberstring

双重断言

  • 任何类型都可以被断言成any
  • any可以被断言成任何类型

所以,可以使用双重断言把任何一个类型断言成任何另一个类型。

function mytest(num: number) {
    return (num as any as string)
}

类型断言不会进行类型转换

类型断言只在TS编译时有效果,在编译结果中会被删除,不会影响到编译结果的类型。

这个例子中,乍一看,断言还同时实现了类型的转换。

但是,都是假象。编译结果,立马打回原形。

所以,需要进行类型转换还是得老老实实直接调用类型转换的方法。

function mytest(num: number) {
    return String(num)
}

const num: number = 1
const str = mytest(num)


console.log(str)
console.log(typeof str)

类型断言VS类型声明

function mytest(num: number): any {
    return num
}

const num = mytest(123) as number
console.log(typeof num)

我们可以使用断言将any类型断言为number类型。

当然我们也可以使用类型声明的方式来实现。

const num: number = mytest(123)

这么一看,结果几乎是一样的。

实际上,类型声明的使用会比类型断言要更严格,所以使用类型断言很可能会导致一些隐藏问题。

先来看看,类型断言和类型声明的核心区别:

  • A断言为B:需要满足A兼容B,或者B兼容A
  • A赋值给B:只有满足B兼容A才行
interface IPerson {
    name: string
}

interface IFishman {
    // 摸鱼人
    name: string,
    play(): void
}

const person: IPerson = {
    name: 'clz'
}

const fishman = person as IFishman

上面的断言:person兼容IFishman,所以IPerson类型能断言为IFishman类型,不过会有一些隐藏问题,比如IFishman
类型原本是需要有play方法的,但是用断言就直接出现了一个没有play方法的IFishman

使用类型声明会更严格,但是也能够避免一些隐藏错误。

const fishman: IFishman = person

目录
相关文章
|
4天前
|
JavaScript 编译器
TypeScript中类型守卫:缩小类型范围的艺术
【4月更文挑战第23天】TypeScript中的类型守卫是缩小类型范围的关键技术,它帮助我们在运行时确保值的精确类型,提升代码健壮性和可读性。类型守卫包括`typeof`(检查原始类型)、`instanceof`(检查类实例)和自定义类型守卫。通过这些方法,我们可以更好地处理联合类型、泛型和不同数据源,降低运行时错误,提高代码质量。
|
4天前
|
JavaScript 前端开发 安全
Apollo与TypeScript:强大类型检查在前端开发中的应用
Apollo与TypeScript:强大类型检查在前端开发中的应用
|
4天前
|
监控 JavaScript 安全
TypeScript在员工上网行为监控中的类型安全实践
本文演示了如何使用TypeScript在员工上网行为监控系统中实现类型安全。通过定义`Website`类型和`MonitoringData`接口,确保数据准确性和可靠性。示例展示了从监控设备获取数据和提交到网站的函数,强调了类型定义在防止错误、提升代码可维护性方面的作用。
36 7
|
3天前
|
JavaScript 前端开发
TypeScript内置类型一览(Record<string,any>等等)(下)
TypeScript内置类型一览(Record<string,any>等等)
|
3天前
|
JavaScript
TypeScript内置类型一览(Record<string,any>等等)(中)
TypeScript内置类型一览(Record<string,any>等等)
|
3天前
|
JavaScript
TypeScript内置类型一览(Record<string,any>等等)(上)
TypeScript内置类型一览(Record<string,any>等等)
|
4天前
|
JavaScript 安全 前端开发
【TypeScript技术专栏】TypeScript中的类型推断与类型守卫
【4月更文挑战第30天】TypeScript的类型推断与类型守卫是提升代码安全的关键。类型推断自动识别变量类型,减少错误,包括基础、上下文、最佳通用和控制流类型推断。类型守卫则通过`typeof`、`instanceof`及自定义函数在运行时确认变量类型,确保类型安全。两者结合使用,优化开发体验,助力构建健壮应用。
|
4天前
|
JavaScript 前端开发 开发者
【TypeScript技术专栏】TypeScript类型系统与接口详解
【4月更文挑战第30天】TypeScript扩展JavaScript,引入静态类型检查以减少错误。其类型系统包括基本类型、数组等,而接口是定义对象结构的机制。接口描述对象外形,不涉及实现,可用于规定对象属性和方法。通过声明、实现接口,以及利用可选、只读属性,接口继承和合并,TypeScript增强了代码的健壮性和维护性。学习和掌握TypeScript的接口对于大型项目开发至关重要。
|
4天前
|
JavaScript 安全 前端开发
【亮剑】TypeScript 由于其强类型的特性,直接为对象动态添加属性可能会遇到一些问题
【4月更文挑战第30天】本文探讨了在 TypeScript 中安全地为对象动态添加属性的方法。基础方法是使用索引签名,允许接受任何属性名但牺牲了部分类型检查。进阶方法是接口扩展,通过声明合并动态添加属性,保持类型安全但可能导致代码重复。高级方法利用 OOP 模式的类继承,确保类型安全但增加代码复杂性。选择哪种方法取决于应用场景、代码复杂性和类型安全性需求。
|
4天前
|
JavaScript 前端开发
TypeScript基础类型
TypeScript基础类型