首页 > 其他分享 >TS中的类型断言

TS中的类型断言

时间:2023-09-27 21:31:32浏览次数:24  
标签:return name TS Cat 类型 any 断言

@TOC

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

使用方法

语法: 1、值 as 类型
2、<类型> 值
不推荐2,因为在tsx(React jsx语法的ts版)中 \语法表示的是一个ReactNode

// 将一个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,并不会不报错

interface Cat{
  name: string;
  run(): void
}
interface Fish {
  name:string;
  swim(): void
}
function getName ( animal: Cat | Fish) {
  return animal.name
}
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'.

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

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

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 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误

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

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

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。
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出来的)

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 实现上述功能就有问题了

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 自然就有问题了,所以也就只能用断言的方式来判断了

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的类型系统运转良好,每个值的类型都需要具体精确,当我们引用一个在此类型上不存在的属性或方法时就会报错

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

如上,报错是非常有用的,可以在变成过程中就提示到我们。 当我们想要在window上挂在一些属性时(我们并不像让其报错)

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

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

(window as any).fool = 'zs'

注意:上面两个例子在表达一个意思 需要注意的是:将一个变量断言为 any 可以说是解决 TypeScript 中类型问题的最后一个手段。 它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any。 上面的例子中,我们也可以通过[扩展 window 的类型(TODO)][]解决这个错误,不过如果只是临时的增加 foo 属性,as any 会更加方便。 总之,一方面不能滥用 as any,另一方面也不要完全否定它的作用,需要在类型的严格性和开发的便利性之间掌握平衡(这也是 TypeScript 的设计理念之一),才能发挥出 TypeScript 最大的价值。

将any断言为一个具体的类型

日常编码中,不可避免的需要处理 any 类型的变量,它们可能是由于第三方库未能定义好自己的类型,也有可能是历史遗留的或其他人编写的烂代码,还可能是受到 TypeScript 类型系统的限制而无法精确定义类型的场景

遇到代码中的any怎么办? 1、可以选择无视它,任由其滋生更多的any 2、也可以选择改进它,通过类型断言及时的把any类型断言为精确的类型,使代码向着更高可维护性的目标发展 demo:

function getCacheData(key: string): any {
    return (window as any).cache[key];
}

改进

function getCacheData(key: string): any {
    return (window as any).cache[key];
}
interface Cat {
    name: string;
    run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();

调用完 getCacheData 之后,立即将它断言为 Cat 类型。这样的话明确了 tom 的类型,后续对 tom 的访问时就有了代码补全,提高了代码的可维护性

类型断言的限制

上面例子得出如下结论:

  • [x] 联合类型可以被断言为其中一个类型
  • [x] 父类可以被断言为子类
  • [x] 任何类型都可以被断言为 any
  • [x] any 可以被断言为任何类型

规则: 错误的说法:是不是任何一个类型都可以被断言为任何另一个类型呢 。 正确解答:并不是任何一个类型都可以被断言为任何另一个类型,具体来说,若 A 兼容 B,那么 A 能够被断言为 B,B 也能被断言为 A

interface Animal {
  name: string;
}
interface Cat1 {
  name: string;
  run(): void;
}

let tom1: Cat1 = {
  name: 'Tom',
  run: () => { console.log('run') }
};
let tom2: Animal = {
  name: 'tom2'
}

function isCat(k: Animal) {
  if (typeof (k as Cat1).run === 'function') {
      return true;
  }
  return false;
}
let isFlag = isCat(tom1)
console.log(isFlag)

function isAnimal(k: Cat1) {
  if (typeof (k as Animal).name === 'string') {
      return true;
  }
  return false;
}

TypeScript 是结构类型系统,类型之间的对比只会比较它们最终的结构,而会忽略它们定义时的关系

上面的例子中,Cat1包含了 Animal 中的所有属性,除此之外,它还有一个额外的方法 run。TypeScript 并不关心 Cat1 和 Animal 之间定义时是什么关系,而只会看它们最终的结构有什么关系——所以它与 Cat extends Animal 是等价的

完全可以这么写:

interface Animal {
    name: string;
}
interface Cat1 extends Animal {
    run(): void;
}

那么也不难理解为什么 Cat 类型的 tom 可以赋值给 Animal 类型的 animal 了——就像面向对象编程中可以将子类的实例赋值给类型为父类的变量,换成TS中更专业的说法,即:Animal 兼容 Cat

interface Animal {
    name: string;
}
interface Cat {
    name: string;
    run(): void;
}

let tom: Cat = {
    name: 'Tom',
    run: () => { console.log('run') }
};
let animal: Animal = tom;

当 Animal 兼容 Cat 时,它们就可以互相进行类型断言了

interface Animal {
    name: string;
}
interface Cat {
    name: string;
    run(): void;
}

function testAnimal(animal: Animal) {
    return (animal as Cat);
}
function testCat(cat: Cat) {
    return (cat as Animal);
}

标签:return,name,TS,Cat,类型,any,断言
From: https://blog.51cto.com/u_16283202/7629290

相关文章

  • java数据类型拓展
    java数据拓展publicclassdemo3{publicstaticvoidmain(String[]args){//整数拓展:进制二进制0b十进制八进制0十六进制0xinti1=10;inti2=010;inti3=0x10;System.out.println(i1);Syste......
  • Java数据类型
    Java数据类型摘自狂神说java的PPT什么是字节1bit表示一位1Byte表示一个字节1024B=1KB1024KB=1M1024M=1G......
  • 后端传递Timestamp类型时间前端自定义接收格式
    Vue项目中处理后端返回日期字符串在这个Vue项目中,后端接口RentalQueryAllServlet返回的租车记录数据中,有一个rentalTime字段,其值是日期字符串,如:"Sep27,20239:23:40AM"。1.获取数据组件中使用axios调用接口获取数据: js methods:{fetchData(){axio......
  • DISCO Presents Discovery Channel Code Contest 2020 Qual
    A-DDCCFinals直接模拟即可。#include<iostream>#include<cstdio>usingnamespacestd;intx,y;intmain(){ scanf("%d%d",&x,&y); intans=0; if(x==1)ans+=30; elseif(x==2)ans+=20; elseif(x==3)ans+=10; if(y==1)ans+=30; ......
  • SQL注入 --【过滤or(包含information、orderby这种也寄了)和and的类型】
    来自:[SWPU2019]Web1详见博客:https://blog.csdn.net/plant1234/article/details/124205120payload如下:查看行数1'/**/group/**/by/**/1,'1......直到1'/**/group/**/by/**/23,'1报错也就是说有22行找出回显点//找出回显点-1'/**/union/**/select/**/1,2,3,4,5,6,7......
  • Springboot+Echarts(五)
    Mybatis-plus操作的多表查询和分页查询今天首先回顾了之前所学的基本的单表增删改查这里想再自己写一遍是为了方便自己以后的开发流程顺利首先创建Springboot项目,选择java8和Springboot2.x.x之后选择Web依赖然后添加依赖<dependency><groupId>com.baomidou<......
  • C. Anya and Ghosts(Codeforces Round #288 (Div. 2))
    #include<iostream>#include<algorithm>#include<stdio.h>#include<string.h>#include<stdlib.h>usingnamespacestd;structnode{intx;inty;}q[3010];inta[3001];intn,m,k;intmain(){while(scanf("......
  • C. Anya and Ghosts( Codeforces Round #288 (Div. 2))
    #include<iostream>#include<algorithm>#include<stdio.h>#include<string.h>#include<stdlib.h>usingnamespacestd;structnode{intx;inty;}q[3010];inta[3001];intn,m,k;intmain(){while(scanf("......
  • ITSM有哪些好用的软件?
    ITSM知名软件有很多,但好用且性价比高的仅有一些,比如:1. AtlassianJira最初,开发人员创建Jira来跟踪软件创建,但随后Atlassian的管理层注意到一些团队正在调整Jira来处理服务台请求2. SpiceworksSpiceworks是一款免费的IT管理工具,提供IT资产管理、网络监控、帮助台、IT服务台等......
  • 【从0学习Solidity】7. 映射类型 mapping
    【从0学习Solidity】7.映射类型mapping博主简介:不写代码没饭吃,一名全栈领域的创作者,专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构,分享一些项目实战经验以及前沿技术的见解。关注我们的主页,探索全栈开发,期待与您一起在移动开发的世界中,不断进步和创造!本文收录于......