首页 > 解决方案 > 需要帮助将类型添加到事件总线方法

问题描述

我正在尝试订阅一个活动,但 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' 的类型不兼容。“事件”类型中缺少属性“名称”,但在“配置”类型中是必需的。

您对此问题的解决方法是什么?

标签: typescript

解决方案


这是因为

on('config', configHandler) // TSC ERROR HERE

期望作为第二个参数EventHandlerEventHandler和的参数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 } */) => {});

上面的例子更安全。

在这里,在我的博客中,你可以找到更多的解释


推荐阅读