七爪源码自定义类型守卫
嘿,欢迎阅读我们的 TypeScript Narrowing 系列的另一篇文章。 在这篇文章中,我将解释:类型谓词如何创建自己的警卫如何通过排除创建守卫
这是我们系列的第三篇文章,如果你还没有看过之前的文章,我强烈建议你去看看,它们为收窄提供了坚实的基础。
类型谓词
在上一篇文章中,我们探讨了基本的类型保护运算符。 现在我想向你展示类型保护函数。
例如,如果您需要检查名为 value 的变量是否为字符串,则可以使用 typeof 运算符。 但你也可以做的是创建一个名为 isString() 的函数,它接收一个参数并在给定参数是字符串时返回 true。const isString = (value: any): boolean => typeof value === "string";
还记得上一篇文章中的 formatErrorMessage() 函数吗?const formatErrorMessage = ( value: null | undefined | string | Error | Warning ): string => { const prefix = "Error: "; // If it"s falsy (null, undefined, empty string), return "Unknown" with the prefix if (!value) { return prefix + "Unknown"; } // If it"s a string, return the string with the prefix if (typeof value === "string") { return prefix + value; } // If it"s a Warning, return the Warning.text with the prefix if ("text" in value) { return prefix + value.text; } // If it"s an Error, return the Error.message with the prefix if (value instanceof Error) { return prefix + value.message; } // We will never reach here throw new Error(`Invalid value type`); }; interface Warning { text: string; }
让我们从中删除 typeof 运算符并使用 isString() 代替。const formatErrorMessage = ( value: null | undefined | string | Error | Warning ): string => { const prefix = "Error: "; // If it"s falsy (null, undefined, empty string), return "Unknown" with the prefix if (!value) { return prefix + "Unknown"; } // If it"s a string, return the string with the prefix if (isString(value)) { return prefix + value; } // If it"s a Warning, return the Warning.text with the prefix if ("text" in value) { return prefix + value.text; } // If it"s an Error, return the Error.message with the prefix if (value instanceof Error) { return prefix + value.message; } // We will never reach here throw new Error(`Invalid value type`); }; interface Warning { text: string; }
相同的代码,我们只是在一个函数中隔离了守卫,对吧? 不,它坏了。 TypeScript 没有将类型缩小为字符串,防护不起作用。
事情是这样的,isString() 返回一个布尔值,我们知道这个布尔值的含义。const isString = (value: any): boolean => typeof value === "string";
这意味着参数是一个字符串。 但是 TypeScript 不知道那个布尔值是什么意思,所以让我们教它。
与其说我们的函数返回一个布尔值,不如说我们的函数返回问题的答案:"这个参数是字符串吗?"。
鉴于我们的参数的名称是 value,我们使用以下语法来做到这一点:value 是字符串。const isString = (value: any): value is string => typeof value === "string";
现在 TypeScript 知道 isString() 是一个类型保护并且我们的 formatErrorMessage() 函数可以正确编译。
我们的 isString() 函数的返回类型不再只是一个布尔值,它是一个"类型谓词"。
因此,要制作自定义类型保护,您只需定义一个返回类型谓词的函数。
所有类型谓词都采用 { parameter } is { Type } 的形式。
未知类型
在我们继续之前的快速提示:
如果我们使用未知类型,我们的代码会更安全,而不是在我们的自定义保护参数中使用类型 any。const isString = (value: unknown): value is string => typeof value === "string";
我制作了一个一分钟的视频来解释任何和未知之间的区别,链接在参考资料中。
自定义警卫
让我们通过将 formatErrorMessage() 函数中的所有检查转换为自定义守卫来锻炼我们的知识。
我们已经有了字符串保护,现在我们需要警告、错误和虚假类型的保护。
错误防护
Error 的保护非常简单,我们只是将 instanceof 操作符检查隔离在一个函数中。const isError = (value: unknown): value is Error => value instanceof Error;
警戒卫士
但另一方面,Warning 守卫并不是那么简单。
TypeScript 允许我们使用 in 运算符,因为我们的 value 参数可以是有限数量的类型,并且它们都是对象。const formatErrorMessage = ( value: null | undefined | string | Error | Warning ): string => { const prefix = "Error: "; // If it"s falsy (null, undefined, empty string), return "Unknown" with the prefix if (!value) { return prefix + "Unknown"; } // If it"s a string, return the string with the prefix if (isString(value)) { return prefix + value; } // If it"s a Warning, return the Warning.text with the prefix if ("text" in value) { return prefix + value.text; } // If it"s an Error, return the Error.message with the prefix if (isError(value)) { return prefix + value.message; } // We will never reach here throw new Error(`Invalid value type`); }; interface Warning { text: string; }
但是如果我们创建一个函数并说我们的参数是未知的,那么它可以是任何东西。 包括原始类型,这会引发错误,因为我们只能在对象中使用 in 运算符。interface Warning { text: string; } const isWarning = (value: unknown): value is Warning => "text" in value; // Compilation error
解决方案是在使用 in 运算符之前确保我们的参数是一个有效的对象。 我们还需要确保它不为空。interface Warning { text: string; } const isWarning = (value: unknown): value is Warning => typeof value === "object" && value !== null && "text" in value;
假守卫
对于虚假值守卫,我们首先需要定义一个类型,其值被认为是虚假的。type Falsy = false | 0 | -0 | 0n | "" | null | undefined;
我在这里不包括 NaN,因为 TypeScript 中没有 NaN 类型。type Falsy = false | 0 | -0 | 0n | "" | null | undefined | ~~NaN~~;
NaN 的类型是数字,并非所有数字都是假的,所以这就是我们不处理 NaN 的原因。typeof NaN; //=> number
有一个提议将 NaN 添加为一种类型——以及整数、浮点数和无穷大。 我认为这很好,拥有这些类型会很有帮助。// Proposal type number = integer | float | NaN | Infinity;
我将在参考文献中留下该提案的链接。
无论如何,现在我们有了 Falsy 类型,我们可以创建一个 falsy 值守卫。
请记住,如果一个值在转换为布尔值时被认为是假的,那么它就是假的。 因此,要检查我们的值是否为假,我们可以使用抽象相等来查看它是否被转换为假。type Falsy = false | 0 | -0 | 0n | "" | null | undefined; const isFalsy = (value: unknown): value is Falsy => value == false;
带有自定义警卫的 formatErrorMessage()
就是这样,我们现在拥有了 formatErrorMessage() 函数所需的所有自定义守卫。// FUNCTION const formatErrorMessage = ( value: null | undefined | string | Error | Warning ): string => { const prefix = "Error: "; // If it"s falsy (null, undefined, empty string), return "Unknown" with the prefix if (isFalsy(value)) { return prefix + "Unknown"; } // If it"s a string, return the string with the prefix if (isString(value)) { return prefix + value; } // If it"s a Warning, return the Warning.text with the prefix if (isWarning(value)) { return prefix + value.text; } // If it"s an Error, return the Error.message with the prefix if (isError(value)) { return prefix + value.message; } // We will never reach here throw new Error(`Invalid value type`); };// GUARDS const isString = (value: unknown): value is string => typeof value === "string"; const isError = (value: unknown): value is Error => value instanceof Error; interface Warning { text: string; } const isWarning = (value: unknown): value is Warning => typeof value === "object" && value !== null && "text" in value; type Falsy = false | 0 | -0 | 0n | "" | null | undefined; const isFalsy = (value: unknown): value is Falsy => value == false;
奖励:通过排除缩小范围
在我们结束之前,我想向你展示一些东西。
虚假值的列表是有限的,对吗?1. `false` 2. `0` `-0` `0n` representations of zero 3. ```` `""` `""` empty string 4. `null` 5. `undefined` 6. `NaN` not a number
但另一方面,真实值是无限的。 所有不虚假的价值观都是真实的。
那么,如何为真实值创建类型保护呢?
诚实守卫
诀窍是排除虚假类型。
我们不是检查我们的值是否为真,而是检查它是否_不_假。type Truthy = Exclude; const isTruthy = (value: T): value is Truthy => value == true; // Test const x = "abc" as null | string | 0; if (isTruthy(x)) { x.trim(); // `x: string` }
我经常使用这个技巧,我们将在以后的文章中再次看到它。
结论
参考资料和其他链接如下。
如果您还没有,请在社交媒体上点赞、订阅和关注我们。 这有助于我们成长,从而为您带来更多免费内容。 这是双赢的。
叶黄素是眼睛的救星吗叶黄素是一种在绿色蔬菜和水果中存在的天然抗氧化物质。它是一种类胡萝卜素,能够吸收蓝光和紫外线,并保护眼睛免受光损伤。许多研究表明,叶黄素对眼睛健康确实有好处。研究显示,叶黄素有助于
非遗技艺,让这个新品以假乱真惊蛰后,又到了青团大量上市的时节。如何在激烈的市场竞争中脱颖而出,各大商家也绞尽脑汁各显神通。以素食文化闻名于世的功德林,全新推出素蟹粉肉松青团,以非遗名菜入团,一经上市就广受好评
四喜烤麸美满幸福申城的团圆年夜饭嘎讲究!中国人一年中最重要的节日就是春节,大年夜的年夜饭自然也就成了一年中最重要的一顿大餐。吃饭时一家人必须聚集在一起,菜肴也要比平时丰盛得多,称吃团圆饭,也叫合家欢。传统年夜饭的菜肴一般
超市3大怪酒,导购不爱推,行家却点名要,都是优质佳酿都说世上的事,千奇百怪,其实酒又何尝不是呢?如果你经常去逛超市,会发现货架上有很多怪酒,这些酒虽然都是优质佳酿,但导购都不怎么爱推荐,倒是行家每次去却点名要。至于导购为啥不爱推?原
浙江一对夫妻以收破烂为生,家里办酒席喝的却是茅台,咋回事?正所谓三百六十行,行行出状元。这话我们可以说是一点都不陌生了,从小到大不知道大家听过多少回了。在如今这个高速发展的社会,有着很多的职业和岗位可以去供我们选择。每个岗位的薪资待遇也各
三月之后,内行人爱吃这道菜,清甜又爽口,总吃不腻进入三月以来,各地景区都爆满了,外面都是人山人海,我们终于摆脱疫情的困难时刻,每天都可以自由出行,可以欣赏全国各地的风光,我们欣赏美丽的风景的时候,品尝各地的美味菜肴,但是也要管着
春天,这菜抓紧吃,全身是宝,焯水一拌真鲜美,可惜很多人当草!导语春天,遇到这菜抓紧吃,鲜嫩营养,全身是宝,焯水一拌真鲜美,可惜很多人当草!大家好,我是傻姐美食,伴随着春天的到来,所有的农作物都在不断的发芽生长,到处都是一道亮丽的风景。尤其是
他拥有最出色的身体条件,却在八角笼中历经坎坷!身高1米91臂展达到惊人的1米98,这样的数据,无论是在次中量级,还是放眼整个UFC所有量级,都是非常优秀的身体条件。然而,拥有这一完美数据的兰迪布朗,却并没有取得大家想象中的成就
帐篷草地派对湖州的春天就该这么玩全面权威深度专业春风拂面,鸟语花香灵粮农场房车营地吃住娱满足您关于农场的所有畅想营地为了满足不同的家庭亲子需求设置有湖景亲子榻榻米房湖畔庭院大床房湖畔庭院双床房房间内设施齐全,给人
这趟开往春天花海的列车,刷爆了乐山人的朋友圈!阳春三月,草长莺飞。随着天气转暖,乐山许多地方呈现出繁花似锦的景象。在夹江县青衣街道青衣江村,一趟趟动车从山间呼啸而过,与山谷里盛放的李花不期而遇,形成了一道独特的美丽风景。阳春三
山东济南植此青绿种个春天来源人民网山东频道老师家长和孩子们共同参与植树活动。李天艺摄孩子们播种后,一起浇水。李天艺摄让孩子们播下植绿护绿爱绿的种子。李天艺摄参与植树活动,让孩子们更加亲近自然。李天艺摄3月