关于typescript:TypeScript基础之类型断言

6次阅读

共计 4315 个字符,预计需要花费 11 分钟才能阅读完成。

前言

文中内容基本上参考 https://ts.xcatliu.com/basics/type-assertion.html。

类型断言

TypeScript 中类型断言(Type Assertion)能够用来手动指定一个值的类型,用来笼罩 TS 中的推断。
当 TypeScript 确定赋值有效时,咱们能够抉择应用类型断言来笼罩类型。
留神:如果咱们应用类型断言,赋值总是无效的,所以咱们须要确保咱们是正确的。否则,咱们的程序可能无奈失常运行。

两种执行类型断言的办法:

  • 应用角括号 < 类型 >
  • as 关键字

根本应用

interface IPerson {
  name: string;
  age: number;
}

let person: IPerson = {
  name: 'xman',
  age: 18
} as IPerson

// 或者
let person1: IPerson = < IPerson > {
  name: 'xman',
  age: 18
} 

然而,当你在 JSX 中应用 < 类型 > 值 的断言语法时,会产生语法抵触,因而倡议应用 值 as 类型 的语法来类型断言。

常见的用处

类型断言的常见用处有以下几种:

将任何一个类型断言为 any

当咱们援用一个在某个对象上不存在的属性或办法时,就会报错:

let num: number = 1;
num.split('');      // Property'split'does not exist on type'number'.

以上谬误提醒显然是十分有用的,然而有的时候,咱们能够十分确定这段代码不会出错,比方在 window 全局对象上加上某个属性:

window.foo = 1;     // Property 'foo' does not exist on type 'Window & typeof globalThis'.

TypeScript 编译时会提醒 window 上不存在 foo 属性,因而咱们能够应用 as any长期将 window 断言为 any 类型:

(window as any).foo = 1;

在 any 类型的变量上,拜访任何属性都是容许的。
然而,应用 any 的同时,也须要它极有可能覆盖了真正的类型谬误,所以尽量不要随便应用any。而且滥用类型断言可能会导致运行时谬误。

let a: string = 'anything'; 
(a as any).setName('xman'); 

如以上代码中,尽管应用类型断言后能通过编译,但却无奈防止运行时的谬误。
运行谬误:Uncaught TypeError: a.setName is not a function

其实咱们能够拓展 window 的类型来解决这个谬误:

declare global {
  interface Window {foo: number}
}

只不过长期减少属性的话应用 as any会更加不便。

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

当 TypeScript 不确定一个联结类型的变量到时是哪个类型的时候,咱们只能拜访此联结类型的所有类型中共有的属性或办法:

class Fish {swim () {console.log('游泳~');
  }
  eat () {console.log('进食!');
  }
}

class Bird {fly () {console.log('翱翔~');
  }
  eat () {console.log('进食!');
  }
}

function getSmallPet(): Fish | Bird {return Math.random() > 0.5 ? new Fish() : new Bird()
}
let pet = getSmallPet();

pet.eat();  //  拜访共有属性没故障  

function isFish (animal: Fish | Bird) {
  // Property 'swim' does not exist on type 'Fish | Bird'.
  // Property 'swim' does not exist on type 'Bird'.
  if (typeof animal.swim === 'function') {return true;}
  return false;
} 

此时能够应用类型断言, 将 animal 断言为 Fish,来解决这个问题:

function isFish (animal: Fish | Bird) {if (typeof (animal as Fish).swim === 'function') {return true;}
  return false;
} 

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

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

class ApiError extends Error {code: number = 0;}

class HttpError extends Error {statusCode: number = 200;}

function isApiError (error: Error) {if (typeof (error as ApiError).code === 'number') {return true;}  
  return false;
}

以上代码中,咱们申明了函数 isApiError, 用来判断传入的参数是不是 ApiError 类型,它的参数类型必定是比拟形象的父类 Error。然而父类 Error 中没有 code 属性,如果间接获取 error.code的话就会报错,所以咱们须要应用类型断言获取 (error as ApiError).code.
为什么不应用 instanceof
如果 ApiErrorHttpError不是一个类而是一个 TS 接口类型的话,它在编译后就会被删除,此时就无奈应用 instanceof 来做运行时判断了

interface ApiError extends Error {code: number;}
interface HttpError extends Error {statusCode: number;}

function isApiError(error: Error) {if (error instanceof ApiError) {  // 'ApiError' only refers to a type, but is being used as a value here.
    return true;
  }
  return false;
}

编译后的后果:

"use strict";
function isApiError(error) {if (error instanceof ApiError) { // 'ApiError' only refers to a type, but is being used as a value here.
    return true;
  }
  return false;
}

此时就只能应用类型断言,通过判断是否存在 code 属性,来判断传入的参数是不是 ApiError 了:

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 类型断言为一个具体类型

比方有个函数,其返回值类型为 any:

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

那么咱们在应用它时,最好可能将调用来它之后的返回值断言成一个准确的类型,免得后续操作呈现问题:

interface Animal {
  name: string;
  eat(): void;}

let bird = getCacheData('bird') as Animal;
bird.eat();

在调用完 getCacheData 之后,立刻断言为 Animal 类型,后续对 bird 的拜访时就有了代码补全,这样就进步了代码的可维护性。

类型断言的限度

并不是任何一个类型都能够被断言为任何另一个类型。先来看看 TS 中结构化类型零碎的根本规定:如果两个类型的构造一样,就说它们是相互兼容的,且可相互赋值(即如果类型 x 要兼容类型 y,那么类型y 至多要具备与类型 x 雷同的属性).

interface Animal {name: string;}
interface Fish {
  name: string;
  eat(): void;}

let fish: Fish = {
  name: '鲤鱼',
  eat: () => {console.log('eat~');  
  }  
}

let animal: Animal = fish;      // 没故障

以上代码中,Fish 蕴含了 Animal 中的所有属性,此外,还有一个办法 eat,TS 只会查看 Fish 是否能赋值给 Animal,编译器查看 Animal 中的每个属性,看是否能在 Fish 中也找到对应属性。而并不关怀 Cat 和 Animal 之间定义时是什么关系。
所以以下代码成立:

interface Animal {name: string;}
interface Fish {
  name: string;
  eat(): void;}

function testAnimal (animal: Animal) {return (animal as Fish)
}

function testFish (fish: Fish) {return (fish as Animal)
}

总结:若 A 兼容 B,那么 A 可能被断言为 B,B 也能被断言为 A。

同理,若 B 兼容 A,那么 A 可能被断言为 B,B 也能被断言为 A。

双重断言

应用 as any as xxx
举个例子:

function getEvent (event: Event) {let e = event || (window as any).event as Event;   
}

留神: 能够间接写window.event,写出(window as any).event as Event 只是举个例子而已。

非空断言

TypeScript2.0 中提供的非空断言操作符 (non-null-assertion-operator)
非空断言操作符操作符 能够用于断言操作对象是非 null 和非 undefined 类型。即:x! 将从 x 值域中排除 null 和 undefined 
如:

function handler (arg: string | null | undefined) {
  let str: string = arg!;   // 没故障 
  str.split('');
  // ...  
}

具体可看之前的文章 TypeScript 根底之非空断言操作符、可选链运算符、空值合并运算符 – 掘金 (juejin.cn)

以上 ts 代码均在 https://www.typescriptlang.or… 上运行过,版本为 4.7.2。
最初,如有谬误,欢送各位大佬指导!感激!

参考资料

https://ts.xcatliu.com/basics/type-assertion.html

正文完
 0