前言
文中内容基本上参考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
?
如果ApiError
和HttpError
不是一个类而是一个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