攀爬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

目录
相关文章
|
2月前
|
JavaScript
typeScript进阶(9)_type类型别名
本文介绍了TypeScript中类型别名的概念和用法。类型别名使用`type`关键字定义,可以为现有类型起一个新的名字,使代码更加清晰易懂。文章通过具体示例展示了如何定义类型别名以及如何在函数中使用类型别名。
40 1
typeScript进阶(9)_type类型别名
|
2月前
|
JavaScript
typeScript基础(2)_any任意值类型和类型推论
本文介绍了TypeScript中的`any`任意值类型,它可以赋值为其他任何类型。同时,文章还解释了TypeScript中的类型推论机制,即在没有明确指定类型时,TypeScript如何根据变量的初始赋值来推断其类型。如果变量初始化时未指定类型,将被推断为`any`类型,从而允许赋予任何类型的值。
56 4
|
1月前
|
JavaScript 前端开发 安全
深入理解TypeScript:增强JavaScript的类型安全性
【10月更文挑战第8天】深入理解TypeScript:增强JavaScript的类型安全性
46 0
|
1月前
|
JavaScript 前端开发 开发者
深入理解TypeScript:类型系统与实用技巧
【10月更文挑战第8天】深入理解TypeScript:类型系统与实用技巧
|
2月前
|
JavaScript
typeScript基础(5)_对象的类型-interfaces接口
本文介绍了TypeScript中接口(interfaces)的基本概念和用法,包括如何定义接口、接口的简单使用、自定义属性、以及如何使用`readonly`关键字定义只读属性。接口在TypeScript中是定义对象形状的重要方式,可以规定对象的必有属性、可选属性、自定义属性和只读属性。
41 1
|
2月前
|
存储 JavaScript
typeScript进阶(11)_元组类型
本文介绍了TypeScript中的元组(Tuple)类型,它是一种特殊的数组类型,可以存储不同类型的元素。文章通过示例展示了如何声明元组类型以及如何给元组赋值。元组类型在定义时需要指定数组中每一项的类型,且在赋值时必须满足这些类型约束。此外,还探讨了如何给元组类型添加额外的元素,这些元素必须符合元组类型中定义的类型联合。
47 0
|
2月前
|
JavaScript
typeScript进阶(10)_字符串字面量类型
本文介绍了TypeScript中的字符串字面量类型,这种类型用来限制变量只能是某些特定的字符串字面量。通过使用`type`关键字声明,可以确保变量的值限定在预定义的字符串字面量集合中。文章通过示例代码展示了如何声明和使用字符串字面量类型,并说明了它在函数默认参数中的应用。
37 0
|
7天前
|
JavaScript 安全 前端开发
TypeScript类型声明:基础与进阶
通过本文的介绍,我们详细探讨了TypeScript的基础与进阶类型声明。从基本数据类型到复杂的泛型和高级类型,TypeScript提供了丰富的工具来确保代码的类型安全和可维护性。掌握这些类型声明能够帮助开发者编写更加健壮和高效的代码,提高开发效率和代码质量。希望本文能为您在使用TypeScript时提供实用的参考和指导。
18 2
|
21天前
|
JavaScript 开发者
在 Babel 插件中使用 TypeScript 类型
【10月更文挑战第23天】可以在 Babel 插件中更有效地使用 TypeScript 类型,提高插件的开发效率和质量,减少潜在的类型错误。同时,也有助于提升代码的可理解性和可维护性,使插件的功能更易于扩展和升级。
|
1月前
|
JavaScript 前端开发
TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第11天】TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!