我有一些如下所示的代码:
type UserTypes = "user" | "super-user" | "admin" ;
function getUserType() : UserTypes {
}
function getAdminPrivs(userType: "user" | "super-user") : null
function getAdminPrivs(userType: "admin") : string
// nb. we need to double declare the final line
// see: https://stackoverflow.com/questions/70146081/why-does-an-overloaded-function-declaration-sometimes-force-useless-type-narrowi
function getAdminPrivs(userType: UserTypes) : null | string
function getAdminPrivs(userType: UserTypes) : null | string {
if(userType === "admin"){
return "hello"
}
return null;
}
// "user" | "super-user" | "admin"
const userType = getUserType();
// string| null
const result = getAdminPrivs(userType);
if(userType === "user" || userType === "super-user"){
}
else {
// string | null
// I really want this to be just string
result
}
在这里,我希望 TypeScriptresult
通过检查 的类型来缩小 的类型userType
。这可能吗?
举个例子,如果我稍微变换一下代码:
const userType = getUserType();
if(userType === "user" || userType === "super-user"){
// null
const result = getAdminPrivs(userType);
}
else {
//string
const result = getAdminPrivs(userType);
}
这在语义上是类似的,但是我们获得了类型缩小的优势。(我无法在实际用例中使用这种技术,因为它getAdminPrivs
实际上是一个 React hook,并且不允许有条件地调用 React hook)
TypeScript 的缩小功能仅在明确编程到类型检查器中的特定情况下有效。它的控制流分析相当强大,但只能在“向前”方向上工作。也就是说,给定的检查可以缩小检查后其余范围的值,但不能返回并重新解释先前分析的代码。除了最简单的程序外,这对于所有程序来说都非常糟糕。例如
之所以有效,是因为
userType
可以缩小,并且随后的使用userType
反映了这种缩小。但是在 TypeScript 中不可能工作,因为它必须缩小范围
userType
,然后在所有先前的语句中追溯缩小范围userType
,然后重新分析的返回类型getAdminPrivs
,并重新分析的类型result
。人类很容易进行这样的分析,因为你知道你在寻找什么。但想象一下,编译器需要付出多少努力来分析一个任意程序,其中对任何值的每次检查都会影响整个程序中该值的类型,这会影响依赖于该值的任何东西的类型,这会影响依赖于这些值的任何事物的类型,等等。这基本上无法以任何可扩展的方式实现,所以这是 TypeScript 的一个根本限制。
有关此限制的来源,请参阅microsoft/TypeScript#41926,其中TS 团队开发负责人表示
另请参阅microsoft/TypeScript#56221 上的类似评论:
最后一点本质上是说,如果你编写了 TS 无法分析的代码,那么对于人类来说可能也会更难,如果你重写它(例如,通过由于 React hook 限制而无法编写的冗余形式),人类和 TS 都会更高兴。
我不知道以下内容是否适用于您的用例,但您可以将其重新打包为判别联合
userType
的判别属性,如下所示:然后,您可以对其进行解构,并使用结果为您提供预期的分析:
是的,这仍然有点多余,但至少你没有
getAdminPrivs()
多次调用。游乐场链接到代码