typescript - 需要帮助将类型添加到事件总线方法
问题描述
我正在尝试订阅一个活动,但 TSC 给了我一个错误(ts playground)。
interface Event {
[key: string]: string;
}
interface EventHandler {
(event: Event): void;
}
interface Config {
name: string;
}
function on (eventName: string, eventHandler: EventHandler) {
console.log(eventName, eventHandler)
}
function configHandler (config: Config) {
console.log(config)
}
on('config', configHandler) // TSC ERROR HERE
错误
'(config: Config) => void' 类型的参数不可分配给 'EventHandler' 类型的参数。参数 'config' 和 'event' 的类型不兼容。“事件”类型中缺少属性“名称”,但在“配置”类型中是必需的。
您对此问题的解决方法是什么?
解决方案
这是因为
on('config', configHandler) // TSC ERROR HERE
期望作为第二个参数EventHandler
。EventHandler
和的参数configHandler
不能相互分配。
declare var config: Config;
declare var ev: Event;
config = ev // error
ev = config // error
顺便说一句,Event
在 TS 全局变量中定义。它是一种内置类型。
您确定要通过索引签名来扩展它吗?
为了使其工作,您应该Event
通过以下方式扩展类型Config
:
interface Event extends Config {
[key: string]: string;
}
无论如何,这似乎不是类型安全的。
看看这个例子:
const enum Events {
foo = "foo",
bar = "bar",
baz = "baz",
}
/**
* Single sourse of true
*/
interface EventMap {
[Events.foo]: { foo: number };
[Events.bar]: { bar: string };
[Events.baz]: { baz: string[] };
}
type Values<T> = T[keyof T];
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type EmitRecord = {
[P in keyof EventMap]: (name: P, data: EventMap[P]) => void;
};
type ListenRecord = {
[P in keyof EventMap]: (
name: P,
callback: (arg: EventMap[P]) => void
) => void;
};
type MakeOverloadings<T> = UnionToIntersection<Values<T>>;
type Emit = MakeOverloadings<EmitRecord>;
type Listen = MakeOverloadings<ListenRecord>;
const emit: Emit = <T,>(name: string, data: T) => {};
emit(Events.bar, { bar: "1" });
emit(Events.baz, { baz: ["1"] });
emit("unimplemented", { foo: 2 }); // expected error
const listen: Listen = (name: string, callback: (arg: any) => void) => {};
listen(Events.baz, (arg /* { baz: string[] } */) => {});
listen(Events.bar, (arg /* { bar: string } */) => {});
上面的例子更安全。
在这里,在我的博客中,你可以找到更多的解释
推荐阅读
- c# - 已解决:当比例不是 100% 时,ContextMenu 不适用于 WinForms 中的 WPF
- django - 如果 memcached 不工作,如何停止 Django
- java - 使用 Springboot 在 Oracle、MySQL 数据库中创建用户 - Spring Data JPA
- python - 使用字符串数据框在 Python 中绘制混淆矩阵
- angular - ng build 后如何在本地运行 ng serve
- sql - 按循环日历 SQL/Oracle 统计数据
- java - 如何调用另一个 .java 文件中存在的类(不是嵌套类)?
- drupal - 如何访问自定义视图树枝中的字段值。德鲁巴8?
- azure - 如何捕获 Azure FunctionApp 的 OutOfMemoryException
- celery - 我想删除所有花芹菜历史/日志,但它不起作用