一文带你了解TypeScript类型断言与类型保护 た 入场券 2022-12-27 09:06 184阅读 0赞 ### 目录 ### * * * 本文概览: * 1. 类型断言 * 2. 双重断言 * 3. 类型保护 * * (1)自定义类型保护 * (2)typeof 类型保护 * (3)instanceof 类型保护 ### 本文概览: ### ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMDMzNTY3_size_16_color_FFFFFF_t_70_pic_center] ### 1. 类型断言 ### TypeScrip的类型系统很强大,但是有时它是不如我们更了解一个值的类型。这时,我们更希望TypeScript不要帮我们进行类型检查,而是让我们自己来判断,则就用到了类型断言。 使用类型断言可以手动指定一个值的类型。类型断言像是一种类型转换,它把某个值强行指定为特定类型: const getLength = target => { if (target.length) { return target.length; } else { return target.toString().length; } }; 这个函数接收一个参数,并返回它的长度。这里传入的参数可以是字符串、数组或是数值等类型的值,如果有 length 属性,说明参数是数组或字符串类型,如果是数值类型是没有 length 属性的,所以需要把数值类型转为字符串然后再获取 length 值。现在我们限定传入的值只能是字符串或数值类型的值: const getLength = (target: string | number): number => { if (target.length) { // error 类型"string | number"上不存在属性"length" return target.length; // error 类型"number"上不存在属性"length" } else { return target.toString().length; } }; 当TypeScript不确定一个联合类型的变量到底是哪个类型时,就只能访问此联合类型的所有类型里共有的属性或方法,所以现在加了对参数target和返回值的类型定义之后就会报错。 这时候,我们就用到了断言,将target的类型断言成string类型。它有两种写法,一种是`<type>value`,一种是`value as type`: // 这种形式是没有任何问题的写法,建议始终使用这种形式 const getStrLength = (target: string | number): number => { if ((target as string).length) { return (target as string).length; } else { return target.toString().length; } }; // 这种形式在JSX代码中不可以使用,而且也是TSLint不建议的写法 const getStrLength = (target: string | number): number => { if ((<string>target).length) { return (<string>target).length; } else { return target.toString().length; } }; 类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的。 **注意:** 类型断言不要滥用,在万不得已的情况下使用要谨慎,因为强制把某类型断言会造成 TypeScript 丧失代码提示的能力。 ### 2. 双重断言 ### 虽然类型断言是有强制性的,但并不是万能的,因为一些情况下也会失效: interface Person { name: string; age: number; } const person = 'ts' as Person; // Error 这个时候会报错,很显然不能把 `string` 强制断言为一个接口 `Person` ,但是并非没有办法,此时可以使用双重断言: interface Person { name: string; age: number; } const person = 'ts' as any as Person; // ok 先把类型断言为 `any` ,再接着断言为想断言的类型就能实现双重断言,当然上面的例子肯定说不通的,双重断言我们也更不建议滥用,但是在一些少见的场景下也有用武之地。 ### 3. 类型保护 ### 类型保护实际上是一种错误提示机制,类型保护是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内。类型守保护主要思想是尝试检测属性、方法或原型,以确定如何处理值。 先来看一个例子: const valueList = [123, "abc"]; const getRandomValue = () => { const number = Math.random() * 10; // 这里取一个[0, 10)范围内的随机值 if (number < 5) { return valueList[0]; // 如果随机数小于5则返回valueList里的第一个值,也就是123 }else { return valueList[1]; // 否则返回"abc" } }; const item = getRandomValue(); if (item.length) { // error 类型“number”上不存在属性“length” console.log(item.length); // error 类型“number”上不存在属性“length” } else { console.log(item.toFixed()); // error 类型“string”上不存在属性“toFixed” } 这个例子中,getRandomValue 函数返回的元素是不固定的,有时返回数值类型,有时返回字符串类型。我们使用这个函数生成一个值 item,然后接下来的逻辑是通过是否有 length 属性来判断是字符串类型,如果没有 length 属性则为数值类型。在 js 中,这段逻辑是没问题的,但是在 TS 中,因为 TS 在编译阶段是无法知道 item 的类型的,所以当在 if 判断逻辑中访问 item 的 length 属性时就会报错,因为如果 item 为 number 类型的话是没有 length 属性的。 这个问题可以通过上面说的类型断言来解决,修改判断逻辑即可: if ((<string>item).length) { console.log((<string>item).length); } else { console.log((<number>item).toFixed()); } #### (1)自定义类型保护 #### 上面的代码不报错是因为通过使用类型断言,告诉 TS 编译器,if 中的 item 是 string 类型,而 else 中的是 number 类型。这样做虽然可以,但是需要在使用 item 的地方都使用类型断言来说明,显然有些繁琐,所以就可以使用类型保护来优化。 可以使用**自定义类型保护**来解决: const valueList = [123, "abc"]; const getRandomValue = () => { const number = Math.random() * 10; // 这里取一个[0, 10)范围内的随机值 if (number < 5) return valueList[0]; // 如果随机数小于5则返回valueList里的第一个值,也就是123 else return valueList[1]; // 否则返回"abc" }; function isString(value: number | string): value is string { const number = Math.random() * 10 return number < 5; } const item = getRandomValue(); if (isString(item)) { console.log(item.length); // 此时item是string类型 } else { console.log(item.toFixed()); // 此时item是number类型 } 首先定义一个函数,函数的参数 value 就是要判断的值,在这个例子中 value 的类型可以为 number 或 string,函数的返回值类型是一个结构为 `value is type` 的类型谓语,value 的命名无所谓,但是谓语中的 value 名必须和参数名一致。而函数里的逻辑则用来返回一个布尔值,如果返回为 true,则表示传入的值类型为`is`后面的 type。 使用类型保护后,if 的判断逻辑和代码块都无需再对类型做指定工作,不仅如此,既然 item 是 string 类型,则 else 的逻辑中,item 一定是联合类型两个类型中另外一个,也就是 number 类型。 #### (2)typeof 类型保护 #### 但是这样定义一个函数来用于判断类型是字符串类型,难免有些复杂,因为在 JavaScript 中,只需要在 if 的判断逻辑地方使用 typeof 关键字即可判断一个值的类型。所以在 TS 中,如果是基本类型,而不是复杂的类型判断,可以直接使用 typeof 来做类型保护: if (typeof item === "string") { console.log(item.length); } else { console.log(item.toFixed()); } 这样直接写效果和自定义类型保护一样。但是在 TS 中,对 typeof 的处理还有些特殊要求: * 只能使用`=`和`!`两种形式来比较 * type 只能是`number`、`string`、`boolean`和`symbol`四种类型,在 TS 中,只会把这四种类型的 typeof 比较识别为类型保护 如果使用`typeof {} === ‘object’`,那它只是一条普通的 js 语句,不具有类型保护具有的效果: const valueList = [{ }, () => { }]; const getRandomValue = () => { const number = Math.random() * 10; if (number < 5) { return valueList[0]; } else { return valueList[1]; } }; const res = getRandomValue(); if (typeof res === "object") { console.log(res.toString()); } else { console.log(ress()); // error 无法调用类型缺少调用签名的表达式。类型“{}”没有兼容的调用签名 } #### (3)instanceof 类型保护 #### `instanceof`操作符是 JS 中的原生操作符,它用来判断一个实例是不是某个构造函数创建的,或者是不是使用 ES6 语法的某个类创建的。在 TS 中,使用 instanceof 操作符同样会具有类型保护效果,来看例子: class CreateByClass1 { public age = 18; constructor() { } } class CreateByClass2 { public name = "TypeScript"; constructor() { } } function getRandomItem() { return Math.random() < 0.5 ? new CreateByClass1() : new CreateByClass2(); // 如果随机数小于0.5就返回CreateByClass1的实例,否则返回CreateByClass2的实例 } const item = getRandomItem(); if (item instanceof CreateByClass1) { // 这里判断item是否是CreateByClass1的实例 console.log(item.age); } else { console.log(item.name); } 这个例子中 if 的判断逻辑中使用 instanceof 操作符判断了 item 。如果是 CreateByClass1 创建的,那么它应该有 age 属性,如果不是,那它就有 name 属性。 **总结:** 通过使用类型保护可以更好地指定某个值的类型,可以把这个指定理解为一种强制转换,这样编译器就能知道这个值是指定的类型,从而符合预期。**typeof** 和 **instanceof** 是JavaScript 中的两个操作符,用来判断某个值的类型和一个值是否是某个构造函数的实例,它们在 TypeScript 中会被当做类型保护。我们也可以自定义类型保护,通过定义一个返回值类型是"参数名 is type"的语句,来指定传入这个类型保护函数的某个参数是什么类型。如果只是简单地要判断某个值是什么类型,使用 typeof 类型保护就可以。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMDMzNTY3_size_16_color_FFFFFF_t_70_pic_center]: /images/20221120/c8c1ca64f1854e42bfbe2bbf74a4fb69.png
相关 一文带你了解TypeScript类型兼容性 目录 本文概览: 1. 什么是类型兼容性? 2. 函数类型兼容性 (1)函数参数 叁歲伎倆/ 2022年12月31日 11:28/ 0 赞/ 172 阅读
相关 一文带你了解TypeScript函数类型 目录 本文概览: 1. 函数类型 (1)为函数定义类型 (2)完整的函数 ╰+哭是因爲堅強的太久メ/ 2022年12月27日 01:25/ 0 赞/ 262 阅读
相关 一文带你了解TypeScript接口使用 目录 本文概览: 1. 接口的基本用法 2. 接口的可选属性 3. 接口的多余属性检查 ﹏ヽ暗。殇╰゛Y/ 2022年12月27日 01:11/ 0 赞/ 259 阅读
相关 一文带你了解TypeScript数据类型 目录 本文概览: 1. TypeScript的数据类型概述 (1)常见的9种数据类型 清疚/ 2022年12月26日 13:26/ 0 赞/ 236 阅读
相关 typescript类型断言 作用: 通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”,你会比 TypeScript 更了解某个值的详细信息,你清楚的知道一个实体具有比它现有类型更确 女爷i/ 2022年10月06日 12:49/ 0 赞/ 288 阅读
相关 TypeScript - 类型断言 类型断言(Type Assertion)可以用来手动指定一个值的类型。 语法: <类型>值 或 值 as 类型 在 tsx 语法(React 的 jsx 语法的 ts - 日理万妓/ 2021年12月16日 23:59/ 0 赞/ 418 阅读
还没有评论,来说两句吧...