我试图理解Typescript如何允许我推断路由对象的键。以下是我的相关类型:
type Route = {
layout: (props: PropsWithChildren) => EmotionJSX.Element;
prefix: string;
routes: Record<Uppercase<string>, Page>;
}
const auth = {
layout: AuthLayout,
prefix: "/auth",
routes: {
LOGIN: {
path: "/login",
component: lazy(() => import("../pages/Login"))
},
}
} satisfies Route;
const user = {
layout: UserLayout,
prefix: "/user",
routes: {
DASHBOARD: {
path: "/dashboard",
component: lazy(() => import("../pages/User/Dashboard"))
},
}
} satisfies Route;
export { user, auth };
所以我正在使用一个getRoutePath()函数,它有两个参数:路由类型和路由名称。该函数可以将路由类型(user或auth)之一作为第一个参数,将路由属性的一个键作为第二个参数,并返回路由的路径。我已经成功地编写了这些函数,它工作正常,它推断了路由类型和路由对象的键。
import ROUTES from "./constants/routes"
const getRoutePath= <T extends keyof typeof ROUTES, R extends keyof typeof ROUTES[T]["routes"]>(type: T, name: R): string | false => {
if (!ROUTES[type]) return false;
if (!params) params = {};
let path = "";
if (ROUTES[type].prefix) path = ROUTES[type].prefix;
if (ROUTES[type].routes[name].path) {
^^^^^^^^^^^^^^^^^^^^^^^^^ => This is throwing an error
path += ROUTES[type].routes[name].path;
}
return path;
}
我可以成功地使用getRoutePath(“admin”,“DASHBOARD”),而不是getRoutePath(“admin”,“NOTHING”)。然而,ts说函数中有一个错误:类型R不能用于索引类型{ DASHBOARD:{ path:string;〈()=〉Element〉;联系我们|{ ...}|{ ...}
我不明白为什么TS在这里抛出一个错误。
我尝试过使用强类型路由而不是使用satisfies,但当我这样做时,我再也无法推断函数的第二个参数。我尝试过创建一个makeRoute函数来强制函数定义,但我仍然得到相同的错误。
这里是一个链接到一个可复制的例子:Typescriptland.orgplay
1条答案
按热度按时间fjaof16o1#
接下来我将使用
好方便讨论
问题是,当你用一个特定的键索引到一个generic类型的值时,编译器倾向于首先将泛型的类型扩展到它的约束(如在microsoft/TypeScript#33181的注解中提到的)。在
getRoutePath()
内部,这意味着虽然routes[type]
被视为泛型类型Routes[T]
,但routes[type].routes
被扩展到union类型:此时编译器将无法跟踪该类型与
name
的类型R extends keyof Routes[T]["routes"]
之间的相关性,并且不会让您索引routes[type].routes[name]
。这类似于microsoft/TypeScript#30581报告的问题,其中有两个union类型的值,它们以始终兼容的方式相互关联,但编译器无法理解。
修复它的最佳方法取决于你的用例。如果你确信你做了正确的事情,只是想让编译器停止抱怨,你可以通过Assert来解决:
routes[type].routes
在R
索引处有一个{path: string}
兼容的值,所以你可以告诉编译器,然后继续。编译器不能 * 验证 * 这一点,所以你已经把一些类型安全的责任从编译器身上拿走了。如果你不想这样做,并且希望编译器能够很好地遵循逻辑,以便在你出错时发出抱怨,那么你需要以类似于microsoft/TypeScript#47109中描述的方式进行重构,这是处理相关联合的推荐方法,如microsoft/TypeScript#30581中所述。
本质上,你需要重写你的类型,使它们显式地以泛型indexed accesses的形式变成mapped types。在你的例子中,它可能看起来像这样:
在这里,我们创建了一个
RouteRoutes
类型,它是从Routes
的键到routes
属性的键的简单Map。然后_Routes
是Routes
的一个版本,它将RouteRoutes
中的关系显式捕获为Map类型。我们可以将routes
赋值给Routes
类型的变量_routes
。然后我们重构
getRoutePath
的R
类型,将其约束为RouteRoutes[T]
:现在,在函数内部,如果我们索引到
_routes[type].routes
而不是routes[type].routes
,编译器将其视为R
中的泛型:这意味着你可以用
R
索引它而不会出错:Playground链接到代码