TypeScript进阶(三):断言
文章目录
- 一、前言
- 二、类型断言
- 2.1 利用 as 实现断言
- 2.2 利用 <> 实现断言
- 2.3 应用示例
- 2.3.1 如果 A,B 如果是类并且有继承关系
- 2.3.2 如果 A,B 如果是类,但没有继承关系
- 2.3.3 如果 A 是类,B 是接口,并且 A 类实现了 B 接口【implements】,则A 的对象变量可以断言成 B 接口类型,同样 B 接口类型的对象变量也可以断言成A类型 。
- 2.3.4 如果 A 是类,B 是接口,并且 A 类没有实现 B 接口,则断言关系和第2项的规则完全相同。
- 2.3.5 如果 A 是类,B 是 type 定义的数据类型
- 2.3.6 如果 A 是类,B 是 type 定义的数据类型,并且 A 类没有实现 B type定义的数据类型,则断言关系和第2项的规则完全相同。
- 2.3.7 如果 A 是一个函数上参数变量的联合类型,例如 string |number,那么在函数内部可以断言成 string 或number 类型。
- 2.3.8 多个类组成的联合类型断言
- 2.3.9 任何数据类型都可以转换成 any 或 unknown 类型,any 或 unknown 类型也可以转换成任何其他数据类型。
- 三、非空断言
- 四、确定赋值断言
- 五、const 断言
- 六、拓展阅读
一、前言
在TypeScript
中,断言是把两种具有重叠关系的数据类型进行相互转换的一种 TS 语法,其可以把其中的一种数据类型转换成另外一种数据类型。类型断言和类型转换产生的效果一样,但语法格式不同。
二、类型断言
2.1 利用 as 实现断言
语法 : 数据 as 类型
const box = document.querySelector('.box')
console.log((box as HTMLDivElement).innerHTML)
2.2 利用 <> 实现断言
语法 : <类型>数据
const box = document.querySelector('.box')
console.log((<HTMLDivElement>box).innerHTML)
这就是对一个数据进行类型断言,也就是在开发的过程中将某一个数据断定为某一个类型,这样就可以直接按照该数据类型去调用属性和方法。
2.3 应用示例
2.3.1 如果 A,B 如果是类并且有继承关系
【 extends 关系】无论 A,B 谁是父类或子类, A 的对象变量可以断言成 B 类型,B 的对象变量可以断言成A类型 。但注意一般在绝大多数场景下都是把父类的对象变量断言成子类。
class People {
public myusername!: string;
public myage!: number;
public address!: string
public phone!: string
constructor() {
}
eat() {
}
step() {
console.log("People=>step");
}
}
class Stu extends People {
public username!: string
public age!: number;
public address!: string
constructor(username: string, age: number, address: string, public phone: string) {
super();
this.address = address;
}
study() {
}
}
let people = new People()
//let result = people as Stu;// 类型断言 正确
let result = <Stu>people;// 类型转换 正确
result.study();
let stu = new Stu("wangwu", 23, "北京", "123")
let result2 = stu as People;// 正确
2.3.2 如果 A,B 如果是类,但没有继承关系
两个类中的任意一个类的所有 public
实例属性【不包括静态属性】加上所有的 public
实例方法和另一个类的所有 public
实例属性加上所有的 public
实例方法完全相同或是另外一个类的子集,则这两个类可以相互断言,否则这两个类就不能相互断言。
// 类型断言中的不能相互重叠问题:
// 两个类之间断言的规则:
// 两个类中任意一个的属性和方法是另一个类的属性和方法完全相同或子集,则这两个类可以相互断言
// 否则这两个类就不能相互断言
class People {
constructor(public username: string, public age: number,
public address: string) {
}
}
class Stu {
public username!: string
public age!: number;
public address!: string
public phone!:string;
constructor(username: string, age: number, address: string) {
this.address = address;
}
}
let people = new People("wangwu", 23, "beijing")
let stuedConvert = people as Stu;
let stu = new Stu("wangwu", 23, "北京")
let peopledConvert = stu as People;
2.3.3 如果 A 是类,B 是接口,并且 A 类实现了 B 接口【implements】,则A 的对象变量可以断言成 B 接口类型,同样 B 接口类型的对象变量也可以断言成A类型 。
interface People {
username: string, age: number, address: string, phone: string
}
class Stu implements People {
public username!: string
public age!: number;
public address!: string
public phone!: string
public kk() {
}
constructor(username: string, age: number, address: string) {
this.address = address;
}
}
let people: People = { username: "wangwu", age: 23, address: "11", phone: "111" }
let result = people as Stu;//正确
let result2 = <Stu>people;// 类型转换 正确
let stu = new Stu("wangwu", 23, "北京")
stu as People;// 正确
2.3.4 如果 A 是类,B 是接口,并且 A 类没有实现 B 接口,则断言关系和第2项的规则完全相同。
两个类中的任意一个类的所有 public
实例属性【不包括静态属性】加上所有的 public
实例方法和另一个类的所有 public
实例属性加上所有的 public
实例方法完全相同或是另外一个类的子集,则这两个类可以相互断言,否则这两个类就不能相互断言。
interface People {
username: string, age: number, address: string, phone: string
}
class Stu {
public username!: string
public age!: number;
public address!: string
public phone!: string
public kk() {
}
constructor(username: string, age: number, address: string) {
this.address = address;
}
}
let people: People = { username: "wangwu", age: 23, address: "11", phone: "111" }
let result = people as Stu;//正确
let stu = new Stu("wangwu", 23, "北京")
stu as People;// 正确
2.3.5 如果 A 是类,B 是 type 定义的数据类型
type
定义的数据类型是引用数据类型,例如 Array
, 对象
,不能是基本数据类型,例如 string
,number
,boolean
,并且有 A 类实现了 B type 定义的数据类型【 implements
】,则 A 的对象变量可以断言成 B type 定义的对象数据类型,同样 B type 定义的对象数据类型的对象变量也可以断言成 A 类型 。
type People = {
username: string, age: number, address: string, phone: string
}
class Stu implements People {
public username!: string
public age!: number;
public address!: string
public phone!: string
constructor(username: string, age: number, address: string) {
this.address = address;
}
}
let people: People = { username: "wangwu", age: 23, address: "11", phone: "111" }
let result = people as Stu;//正确
let stu = new Stu("wangwu", 23, "北京")
stu as People;// 正确
2.3.6 如果 A 是类,B 是 type 定义的数据类型,并且 A 类没有实现 B type定义的数据类型,则断言关系和第2项的规则完全相同。
两个类中的任意一个类的所有 public
实例属性【不包括静态属性】加上所有的 public
实例方法和另一个类的所有 public
实例属性加上所有的 public
实例方法完全相同或是另外一个类的子集,则这两个类可以相互断言,否则这两个类就不能相互断言。
type People = {
username: string, age: number, address: string, phone: string
}
class Stu {
public username!: string
public age!: number;
public address!: string
constructor(username: string, age: number, address: string) {
this.address = address;
}
}
let people: People = { username: "wangwu", age: 23, address: "11", phone: "111" }
let result = people as Stu;//正确
let stu = new Stu("wangwu", 23, "北京")
stu as People;// 正确
2.3.7 如果 A 是一个函数上参数变量的联合类型,例如 string |number,那么在函数内部可以断言成 string 或number 类型。
function selfMutiply(one: string | number) {
one as number +3;
}
2.3.8 多个类组成的联合类型断言
例如:let vechile: Car | Bus | Trunck
。 vechile 可以断言成其中任意一种数据类型。 例如 vechile as Car, vechile as Bus , vechile as Trunck
。
class Vechile {
static count: number = 3;
public brand: string;
public vechileNo: string;
public days: number;
public total: number = 0;
public deposit: number;
constructor(brand_: string, vechileNo_: string, days_: number, deposit_: number) {
this.brand = brand_;
this.vechileNo = vechileNo_;
this.days = days_;
this.deposit = deposit_;
}
public calculateRent() {
}
payDesposit() {
}
public safeShow() {
}
}
class Car extends Vechile {
public type: string;
constructor(brand_: string, vechileNo_: string, days_: number,
deposit_: number, type_: string) {
super(brand_, vechileNo_, days_, deposit_);
this.type = type_;
}
public getPriceByType() {
}
public calculateRent() {
}
public checkIsWeigui(isOverWeight: boolean) {
}
}
class Bus extends Vechile {
public seatNum: number
constructor(brand_: string, vechileNo_: string, days_: number,
deposit_: number, seatNum_: number) {
super(brand_, vechileNo_, days_, deposit_);
this.seatNum = seatNum_;
}
public getPriceBySeatNum() {
}
public checkIsOverNum(isOverWeight: boolean) {
}
}
class Truck extends Vechile {
ton!: number
constructor(brand_: string, type_: string,
days_: number, deposit_: number, ton_: number) {
super(brand_, type_, days_, deposit_);
this.ton = ton_;
}
checkIsOverWeight(isOverWeight: boolean) {
}
CalRentPrice() {
}
public calRent() {
}
public calDesposit() {
}
}
class Customer {
rentVechile(vechile:Bus | Truck | Car) {
// 下面进行断言、转换
//<Bus>vechile <==等同==> vechile as Bus
//vechile as unknown
//vechile.calculateRent()
//(vechile as Bus).checkIsOverNum(false)
}
}
2.3.9 任何数据类型都可以转换成 any 或 unknown 类型,any 或 unknown 类型也可以转换成任何其他数据类型。
function add(one: string | number, two: string | number) {
return one as any + two as any
}
console.log(add(3, 5))
console.log(add("3", 5))
三、非空断言
也就是在开发过程中,忽略有可能出现的 undefined
和 null
类型。
这里我们使用 ! 来做这个事情
const box = document.querySelector('.box')
console.log(box!.innerHTML)
当 box 只有可能是 一个内容 或者 undefined
或者 null
的时候,可以用 !
进行断言,也就是告诉TS,box 不可能是 null
或者 undefined
,利用 !
排除了 undefined
和 null
的可能。
四、确定赋值断言
在开发阶段,还有这样一种情况,在初始定义某一个变量的时候,有可能是不赋值的,在后面的代码或者函数内再对其进行赋值,然后使用。
// 初始化的时候不进行赋值
let n: number
// 通过调用这个函数对 n 进行赋值操作
function init () { n = 100 }
init()
// 这里使用一下 x
console.log(x.toFixed(2))
然后就会发现,TS 又提示错误了。这是因为 TS 不清楚init 函数调用以后会对 x 进行赋值
所以就会告诉你,你还没有赋值呢,就调用了 toFixed 方法
这个时候,我们就可以使用确定赋值断言,还是使用 !
// 初始化的时候不进行赋值
let n!: number
// 通过调用这个函数对 n 进行赋值操作
function init () { n = 100 }
init()
// 这里使用一下 x
console.log(x.toFixed(2))
在初始化的时候,给变量后面加一个 !
,也就是告诉 TS,这个变量我一定会百分之一万赋值的。这样今后在遇到你使用 x 这个变量的时候,TS 就会默认认为他一定有值,也就不会提示错误了。
五、const 断言
这个有点和定义变量的关键字差不多,但是又不太一样,注意不要搞混淆!
let n: number = 100 as const
let s: string = 'hello world' as const
这样一来,我们定义的 n 和 s 就不能被更改了。
const obj = { name: 'Jack', age: 18 }
以上示例就是用 const
定义的一个变量,只不过值是一个引用数据类型。 const
只能保证 obj 不会被改变,但是 obj 内部的子级数据是不会被限制的,如果想让 obj 内的每一个成员都是只读的属性,不允许被更改,只能是在书写接口的时候,把每一个属性都定义为 readonly
。应用const
实现方案如下:
const obj = { name: 'Jack', age: 18 } as const
这样一来,obj 内 所有的子级数据就都不能被更改。
注意⚠️:
const
是ES6
中定义变量的关键字,表明当前定义的是一个常量,不允许被更改,但是对于引用数据类型来说,其子属性还是可以被修改的。as const
是TS 内的语法,告诉 TS,被断言的内容不管是自己本身还是其子属性都不允许被修改,TS 就会对每一个层级的数据进行最严格的判断和限制。
六、拓展阅读
- 《TypeScript 进阶专栏》