TS系列(3): 什么情况下可以使用any?

本篇是「一起学习TypeScript」 系列的第3篇,接下来笔者将持续深耕,不定时更新一系列精彩纷呈的TypeScript文章,旨在解答使用TypeScript过程中的各种疑难杂症,一起探索TS的无穷魅力!

【TS系列回顾】:

TS系列(1): React中能否使子组件实现“类型安全”?

TS系列(2): 使用泛型提升TS函数可重用性

一、any之“毒”

any是TypeScript中一个极其强大的类型,它可以让你像使用JS一样地对待一个值(而不是TS)。这就意味着它禁用了TS的所有特性–类型检查、自动完成和安全性等。

一个例子🌰:

const myFunction = (input: any) => {
  input.someMethod();
};
// This will fail at runtime!
myFunction("abc"); 

频繁使用any通常被认为是有害的,甚至有ESlint Rules用来阻止any的使用,从而让开发人员完全放弃使用any

然而,在一些特定的场景中,any其实是正确的选择。接下来,本文将介绍可以合法使用any的一些场景。

二、可合法使用any的场景

1、类型参数约束

假设我们想要在TS中实现ReturnType程序,它采用函数类型(function type)并返回其返回值的类型。

我们需要创建一个将function type作为类型参数的 泛型类型(generic type)。如果这时我们不能使用any,也许我们会使用unknown,如下所示:

type ReturnType<T extends (...args: unknown[])=> unknown >=
    T extends (...args: unknown[]) => infer R ? R: never;

主要看下代码中的这句**T extends (...args: unknown[]) => unknown** , 它意味着只允许接收参数为unknown[]的数组,并且返回unknown

这么写,对于没有参数的函数来说,上面的ReturnType写法似乎是可行的:

TS系列(3): 什么情况下可以使用any?

但是,当我们向myFunction中增加1个参数,问题出现了:

TS系列(3): 什么情况下可以使用any?

事实上,只有当我们把参数设置为input: unknown时,上面定义的ReturnType才会生效:

TS系列(3): 什么情况下可以使用any?

因此我们创建了一个ReturnType,该函数仅仅适用于接收unknown作为参数的函数。😅😅。这显然不是我们想要的效果,我们的初衷是希望ReturnType对任何函数都起作用!

解决方案:使用**any[]** 改写上面的ReturnType

type ReturnType<T extends (...args: any [] ) =>  any> =
  T extends (...args: any [] ) => infer R ? R : never;

这时,我们再来使用ReturnType,发现它是可以按照我们的期待正常工作的:

TS系列(3): 什么情况下可以使用any?

此种场景下可以安全使用any的原因是:我们故意定义了一个宽类型(wide type),也就是“我不在乎接收什么函数,只要它是一个函数,就可以使用ReturnType”。这时,使用any就是安全的。

2、从泛型函数返回条件类型

在一些场景下,TS的窄化能力(narrowing ability,指通过条件语句、类型守卫等逐步缩小或确定变量的具体类型)并不想我们想的那么好。

假设,我们想要创建一个函数,该函数可以根据不同的条件返回不同的类型:

TS系列(3): 什么情况下可以使用any?

可以看出youSayGoodbyeISayHello函数并没有按照我们期待的那样运行:当我们传"hello"的时候,期待返回的是"goodbye",但是实际返回的结果却是 "hello"|"goodbye" 😒

此时,我们可以使用条件类型来解决这个问题:

TS系列(3): 什么情况下可以使用any?

但是,其实👆🏻这么写也会飘红,实际上:

TS系列(3): 什么情况下可以使用any?

🤯看起来TS似乎没有将条件类型与运行时逻辑相匹配,这个函数不能返回 "hello" 或者 "goodbye" 🤯

当然,我们是可以使用**as**强制使其成为正确的条件类型:

TS系列(3): 什么情况下可以使用any?

为了让代码看起来更简洁,我们可以抽取通用的逻辑到一个泛型类型中:

TS系列(3): 什么情况下可以使用any?

然而,实际上👆🏻这种场景中,使用any更合理。

TS系列(3): 什么情况下可以使用any?

你也许会说,这里使用any会使得我们的函数类型安全性降低。但这种情况下,通常最好是使用**as any的同时再为函数的行为添加单元测试**,两者结合基本可以保证类型安全(且代码没有那么繁琐)。

三、结论

问题仍然存在:你是否应该在你的代码库中禁止使用any

我认为,总的来说,答案是yes。你应该打开ESLint规则来阻止any的使用,并且应该尽可能地避免它。

但是,凡事皆不可一棒子打死,确实存在一些场景使用any更合适。这时,你可以使用eslint-disable去绕过any使用限制 😉

如果你发现了其他可以合法使用或使用**any**更合适的场景,可以在评论区评论哦!

原文链接:https://juejin.cn/post/7353447472557211711 作者:爆浆小丸子

(0)
上一篇 2024年4月3日 下午4:54
下一篇 2024年4月3日 下午5:05

相关推荐

发表回复

登录后才能评论