24、TS中的类型断言

TS中的类型断言

TS的断言还是很有意思的,默认TS会给我们自动判断类型,我们也可以利用断言告诉TS(你别在那瞎推断了,按照我给你说的类型来)

使用方法

语法:

1、值 as 类型

2、<类型> 值

不推荐2,因为在tsx(React jsx语法的ts版)中 <Foo>语法表示的是一个ReactNode

javascript 复制代码
// 将一个any类型的变量str断言为string类型,获取length属性
let str: any = 'hello'
let len: number = (<string>str).length
console.log(len)
let len1:number = (str as string).length
console.log(len1)

用途

将一个联合类型断言为其中一个类型

当ts不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型中所有类型中共有的属性或方法

如下demo,并不会不报错

javascript 复制代码
interface Cat{
  name: string;
  run(): void
}
interface Fish {
  name:string;
  swim(): void
}
function getName ( animal: Cat | Fish) {
  return animal.name
}
javascript 复制代码
interface Cat{
  name: string;
  run(): void
}
interface Fish {
  name:string;
  swim(): void
}
function isFish(animal: Cat | Fish):boolean {
  if(typeof animal.swim === 'function'){
    return true
  }
  return false
}
// 报错: error TS2339: Property 'swim' does not exist on type 'Cat | Fish'.
//  Property 'swim' does not exist on type 'Cat'.

// 备注:就算用上?.一样还是会报相同的错误

上断言:完美解决,不再报错

javascript 复制代码
interface Cat{
  name: string;
  run(): void
}
interface Fish {
  name:string;
  swim(): void
}
function getName ( animal: Cat | Fish) {
  return animal.name
}
function isFish(animal: Cat | Fish): boolean {
  if(typeof (animal as Fish).swim === 'function'){
    return true
  }
  return false
}

注意事项

类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误

javascript 复制代码
interface Cat{
  name: string;
  run(): void
}
interface Fish {
  name:string;
  swim(): void
}
const tomm: Cat = {
  name: 'Tom',
  run() { console.log('run') }
};
function isFish(animal: Cat | Fish): any {
  (animal as Fish).swim()
}
isFish(tomm)

编译后的js

javascript 复制代码
const tomm = {
    name: 'Tom',
    run() { console.log('run'); }
};
function isFish(animal) {
    animal.swim();
}
isFish(tomm);
// 运行直接报错:TypeError: animal.swim is not a function

原因是 (animal as Fish).swim() 这段代码隐藏了 animal 可能为 Cat 的情况,将 animal 直接断言为 Fish 了,而 TypeScript 编译器信任了我们的断言,故在调用 swim() 时没有编译错误。

可是 swim 函数接受的参数是 Cat | Fish,一旦传入的参数是 Cat 类型的变量,由于 Cat 上没有 swim 方法,就会导致运行时错误了。

使用类型断言时一定要格外小心,尽量避免断言后调用方法或引用深层属性,以减少不必要的运行时错误。

将一个父类断言为更加具体的子类

当类之间有继承关系时,类型断言也是很常见的:

以下函数功能,判断继承自A的两个类,具体是谁的由那个类new出来的。这样的话这个函数的参数类型定义应该为A

typescript 复制代码
class ApiError extends Error {
  code: number = 0
}
class Http extends Error {
  statusCode: number = 200
}

function isApiError(error: Error) {
  if(typeof (error as ApiError).code === 'number') {
    return true
  }
  return false
}
// 上面的例子中,我们声明了函数 isApiError,它用来判断传入的参数是不是 ApiError 类型,
// 为了实现这样一个函数,它的参数的类型肯定得是比较抽象的父类 Error,这样的话这个函数就能接受 Error 或它的子类作为参数了。

// 但是由于父类 Error 中没有 code 属性,故直接获取 error.code 会报错,需要使用类型断言获取 (error as ApiError).code。
javascript 复制代码
class ApiError extends Error {
  code: number = 0
}
class Http extends Error {
  statusCode: number = 200
}
function isApiError(error: Error) {
  if(error instanceof ApiError) {
    return true
  }
  return false
}

就功能论功能,上述的实现其实还有另外一种更好的实现(instanceof 本身就是用于实例是不是由某个类new出来的)

javascript 复制代码
class ApiError extends Error {
  code: number = 0
}
class Http extends Error {
  statusCode: number = 200
}
function isApiError(error: Error) {
  if(error instanceof ApiError) {
    return true
  }
  return false
}

当用接口来表达时,再用instanceof 实现上述功能就有问题了

javascript 复制代码
interface ApiError extends Error {
  code: number;
}
interface HttpError extends Error {
  statusCode: number;
}
function isApiError(error: Error) {
  if(error instanceof ApiError) {
    return true
  }
  return false
}
// 报错: error TS2693: 'ApiError' only refers to a type, but is being used as a value here.

接口是一个类型,不是一个真正的值,它在编译结果中会被删除,此时再用instanceof 自然就有问题了,所以也就只能用断言的方式来判断了

javascript 复制代码
interface ApiError extends Error {
  code: number;
}
interface HttpError extends Error {
  statusCode: number;
}
function isApiError(error: Error) {
  if(typeof (error as ApiError).code === 'number') {
    return true
  }
  return false
}

将任何一个类型断言为any

理想情况下,TS的类型系统运转良好,每个值的类型都需要具体精确,当我们引用一个在此类型上不存在的属性或方法时就会报错

javascript 复制代码
const foo: number = 1;
foo.length = 1;
// 报错 28:5 - error TS2339: Property 'length' does not exist on type 'number

如上,报错是非常有用的,可以在变成过程中就提示到我们。

当我们想要在window上挂在一些属性时(我们并不像让其报错)

javascript 复制代码
window.kate = "ZL"
// 报错:error TS2339: Property 'kate' does not exist on type 'Window & typeof globalThis'.

这样解决就好了,不会报错,编译通过,断言为any,any类型上访问任何属性都是允许的

javascript 复制代码
(window as any).fool = 'zs'

注意:上面两个例子在表达一个意思

需要注意的是:将一个变量断言为 any 可以说是解决 TypeScript 中类型问题的最后一个手段。
它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any

上面的例子中,我们也可以通过[扩展 window 的类型(TODO)][]解决这个错误,不过如果只是临时的增加 foo 属性,as any 会更加方便。

总之,一方面不能滥用 as any,另一方面也不要完全否定它的作用,需要在类型的严格性和开发的便利性之间掌握平衡(这也是 TypeScript 的设计理念之一),才能发挥出 TypeScript 最大的价值。

相关推荐
烛阴17 小时前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
奔跑的蜗牛ing2 天前
Vue3 + Element Plus 输入框省略号插件:零侵入式全局解决方案
vue.js·typescript·前端工程化
光影少年2 天前
Typescript工具类型
前端·typescript·掘金·金石计划
开心不就得了2 天前
React 状态管理
react.js·typescript
冷冷的菜哥2 天前
react实现无缝轮播组件
前端·react.js·typescript·前端框架·无缝轮播
lypzcgf3 天前
Coze源码分析-资源库-创建知识库-前端源码-核心组件
前端·typescript·react·coze·coze源码分析·ai应用平台·agent开发平台
患得患失9493 天前
【个人项目】【前端实用工具】OpenAPI to TypeScript 转换器
前端·javascript·typescript
万添裁4 天前
ArkAnalyzer源码初步分析I——分析ts项目流程
typescript·arkanalyzer
凡二人5 天前
Flip-js 优雅的处理元素结构变化的动画(解读)
前端·typescript
烛阴5 天前
【TS 设计模式完全指南】TypeScript 装饰器模式的优雅之道
javascript·设计模式·typescript