我有一个名为的通用类型RouteConfig
,我试图在名为的对象内部使用routes
。
// Generic type
type RouteConfig<Params> = {
path: string;
component: (params: Params) => string;
};
type Route = 'Home' | 'User';
// Object
const routes: Record<Route, RouteConfig<unknown>> = {
Home: {
path: 'a',
// False positive (should not error)
component: (params: { foo: string }) => 'x',
},
User: {
path: 'a',
// False positive (should not error)
component: (params: { bar: string }) => 'z',
},
};
对象内部的每个值都可以为的Params
内部类型具有自己的类型RouteConfig
。
我的问题是:在routes
类型注释中,应该将泛型传递给RouteConfig
什么?
我无法提供单一类型,因为每个对象值都可以具有自己的类型。(一个联合会将相同的联合类型应用于所有对象值,这不是我想要的。)
在上面的示例中,我使用unknown
,但是这会导致错误的肯定类型错误(请参见上面的代码示例中的注释)。
我无法使用,any
因为这样一来,从对象读取数据时我就失去了类型安全性:
const routes: Record<Route, RouteConfig<any>> = {
Home: {
path: 'a',
// True negative
component: (params: { foo: string }) => 'x',
},
User: {
path: 'a',
// True negative
component: (params: { bar: string }) => 'z',
},
};
// True negative
routes.Home.component({ foo: 'abc' });
// False negative (should error)
routes.Home.component({ bar: 'abc' });
我可以从中删除类型注释routes
:
const routes = {
Home: {
path: 'a',
// True negative
component: (params: { foo: string }) => 'x',
},
User: {
path: 'a',
// True negative
component: (params: { bar: string }) => 'z',
},
};
// True negative
routes.Home.component({ foo: 'abc' });
// True positive
routes.Home.component({ bar: 'abc' });
…但是后来我失去了诸如的类型和属性的“查找引用”和“重命名”之类的便利RouteConfig
。此外,由于TypeScript将不再能够检查对象是否包含所有必需的键(由Route
类型定义),因此我将失去类型安全性。
我认为我正在寻找的是一种注释routes
对象类型(泛型除外)的方法-泛型应从对象定义中推断出来。
总而言之,我正在寻找一种编写方法来实现以下所有目的:
RouteConfig
,更改是否适用于所有用法?RouteConfig
吗?有什么办法可以实现上述所有目的?
同时获得所有这三个元素并非完全简单。我使用它的一种方法是使用一个额外的函数进行推理(以捕获对象文字的实际类型),然后对输入和输出类型进行一些重做。通过将输入添加& Record<Route, RouteConfig<any>>
到参数类型以使ts知道ts的输入值RouteConfig<any>
(否则ts会在重命名期间错过这些值),并且输出类型i实质上通过了一个标识类型,以确保对refrefRouteConfig
的保留。输出(没有此用法的站点将在重命名中丢失):
type RouteConfig<Params> = {
path: string;
component: (params: Params) => string;
};
type Route = 'Home' | 'User';
type RouteHelper<T extends Record<Route, RouteConfig<any>>> = {
[P in keyof T] : RouteConfig<T[P] extends RouteConfig<infer P> ? P : never>
}
function createRoutes<T extends Record<Route, RouteConfig<any>>>(r: T & Record<Route, RouteConfig<any>>): RouteHelper<T>{
return r as any;
}
// [✅] Good dev UX
// [✅] No unexpected errors
// [✅] Type safety (errors when expected)
{
const routes = createRoutes({
Home: {
path: 'a',
// True negative
component: (params: { foo: string }) => 'x',
},
User: {
path: 'a',
// True negative
component: (params: { bar: string }) => 'z',
},
});
// True negative
routes.Home.component({ foo: 'abc' });
// True positive
routes.Home.component({ bar: 'abc' });
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句