【发布时间】:2021-12-25 16:08:40
【问题描述】:
我正在尝试编写一个支持事件和子事件的 Sub-Pub 类,由以下语法表示:
publisher.on("message:general", ... ) // subscribe to all messages
publisher.on("message", ... ) // subscribe to messages in general
为此,我使用 TypeScript 模板文字。
问题在于,虽然它可以广泛使用,但它似乎打破了一般约束。我做错了吗?
这是目前的样子:
TS Playground link for convenience
interface ChatEvents {
connect: {
user: string;
};
disconnect: {
user: string;
reason: "banned" | "timeout" | "leave";
};
}
declare type ChatEvent = keyof ChatEvents;
interface SubEvents extends Record<ChatEvent, string> {
connect: "general" | "watercooler" | "lobby";
disconnect: "voice" | "text";
}
declare type EventWithSubEvent<T extends ChatEvent> = `${T}${
| ""
| `:${SubEvents[T]}`}`;
// "connect" | "connect:general" | "connect:watercooler" | "connect:lobby"
declare type ChatConnectEvent = EventWithSubEvent<"connect">;
// "disconnect" | "disconnect:voice" | "disconnect:text"
declare type ChatDisconnectEvent = EventWithSubEvent<"disconnect">;
// So far so good!
// Let's write some generics:
declare function subscribeToEvent<E extends ChatEvent>(
event: E,
callback: (payload: ChatEvents[E]) => void
): void;
subscribeToEvent("connect", (payload) => {
// Correctly extracts matching type
type TPayload = typeof payload; // { user: string; }
});
declare function subscribeToSubEvent<E extends ChatEvent>(
event: EventWithSubEvent<E>,
callback: (payload: ChatEvents[E]) => void
): void;
subscribeToSubEvent("connect:general", (payload) => {
// Extracts all possible payload types instead of only the `connect` one
/*
{
user: string;
} | {
user: string;
reason: "banned" | "timeout" | "leave";
}
*/
type TPayload = typeof payload;
});
subscribeToSubEvent("connect:voice", () => {}) // Should fail but doesn't. `:voice` is a subevent of `disconnect`, not `connect`.
【问题讨论】:
-
我认为问题可以简化到第 18 行,粘贴到下面看看结果类型:
type X = EventWithSubEvent<ChatEvent>这个类型太宽了
标签: javascript typescript generics