我使用TypeScript已经有一段时间了,一直在努力解决这个问题。我正在尝试为我们的应用构建一个事件系统,我希望在创建对象时强制类型安全,这些对象用于将与特定上下文相关的事件分组在一起。
总结
在我详细解释我想要什么之前,让我给你我试图达到的最终结果:
type DashboardGroups = "home" | "settings" | "profile";
// events should only have one of these groups when added to the `DashboardEventsMap`
type DashboardEvent = IMetricsEvent<DashboardGroups>;
// enforce that events match the following shape here: `{ [event:string]: DashboardEvent }`
const DashboardEventsMap = createEventMapping<DashboardEvent>({
validEvent: {
name: "valid event",
group: "home" // ✅ this should work
},
invalidEvent: {
name: "invalid event",
group: "invalid group", // ❌ This should give a type error
}
})
// BUT MOST IMPORTANTLY
// I want to preserve the shape and intellisense when trying to use the map object
DashboardEventsMap.validEvent // ✅ This should both work and show in autocomplete
DashboardEventsMap.eventWhichDoesntExist // ❌ This should give a type error
详细信息
事件具有以下结构,并且必须能够采用自定义组,以强制事件属于应用程序不同部分中的特定组。
export interface IMetricsEvent<
TGroup extends string = string,
> {
name: string;
group?: TGroup;
}
现在,我的createEventMapping
函数遇到了一些问题:
type MetricsEventMapType<TEvent extends IMetricsEvent> = {
[event: string]: TEvent;
};
export const createMetricsEventMapping = <
TMetricsEvent extends IMetricsEvent,
T extends MetricsEventMapType<TMetricsEvent> = MetricsEventMapType<TMetricsEvent>,
>(
arg: T,
) => arg;
1.没有类型
const map = createMetricsEventMapping({event: {name:"event", group:"any group"});
map.event // ✅ autocompletion works but there is no typechecking on the groups
2.传入事件类型
但是如果我传入一个事件类型,我会得到对组的类型检查,但是没有自动完成:
type DashboardEvent = IMetricsEvent<"home">;
const map = createMetricsEventMapping<DashboardEvent>({event: {name:"event", group:"any group"});
map.event // ❌ typechecking works on the groups above ☝️ but there's no autocompletion anymore
3.删除问题= MetricsEventMapType<TMetricsEvent>
我知道发生这种情况的原因是当没有类型被传递时,typescript正确地推断出所有的东西,但是当第一个类型参数TMetricsEvent
被传递时,typescript现在期望T也被传递,因为它是可选的,所以它只是默认为MetricsEventMapType<TMetricsEvent>
而不是被推断出来。
但是,如果我删除默认的= MetricsEventMapType<TMetricsEvent>
,那么当我传入TMetricsEventType
时,typescript就会开始对我大喊大叫(当没有传入类型时,就很好)
export const createMetricsEventMapping = <
TMetricsEvent extends IMetricsEvent,
T extends MetricsEventMapType<TMetricsEvent>, // remove default here
>(
arg: T,
) => arg;
type DashboardEvent = IMetricsEvent<"home">;
// ❌ This now gives a type error saying that expected 2 type arguments but got 1
const map = createMetricsEventMapping<DashboardEvent>({event: {name:"event", group:"any group"});
如果这是不可能的,或者是用更好的类型来代替函数推理、嵌套函数或任何类似的东西的更好的方法,请让我知道。任何帮助都是感激之情!
1条答案
按热度按时间vc6uscn91#
如果您使用的是新版本的TypeScript,则
satisfies
运算符可能是更好的选择:Playground链接
你可以使用一个函数(如果你需要更好地控制推理),但是就像你已经注意到的,TypeScript函数推理是一件全有或全无的事情。你要么让TypeScript推理所有的类型参数,要么指定它们(未指定的参数取自默认值)
您有几种解决方法:
最简单的解决方案是使用函数currying:
Playground链接
您还可以使用某种虚拟助手函数为
TMetricsEvent
创建一个推理站点,作为常规参数:Playground链接