Typescript:引用函数签名中的函数参数

Typescript: Refer to function argument inside the function signature

提问人:cl10k 提问时间:7/18/2022 更新时间:7/18/2022 访问量:234

问:

如何在函数签名中引用特定的函数参数?

enter image description here

上面的图片是假的,只是为了演示!我通过将名称填充到另一个函数中来提供并且需要检索 arg 的一些类型信息。但是我不知道如何在函数签名中引用名称。namearg


上下文:

我正在开发一个简单的事件管理器。您可以定义事件名称,为这些名称分配回调并触发事件。回调可以有任意数量的参数。

(下面的例子非常精简,实际实现比较复杂,比如事件可以有多个回调等)

type CallbackType = (...args: any[]) => void;

const eventRegistry: Map<string, CallbackType> = new Map();

const registerEvent = (name: string, callback: CallbackType): void => {
        eventRegistry.set(name, callback);
}

const fireEvent = (name: string, ...args: any[]): void => {
    eventRegistry.get(name)!(...args);
}

一些示例回调

const foo = (input: number): void => {
    console.log("foo here:", input);
}

const bar = (input1: boolean, input2: string): void => {
    console.log("bar here:", input1, input2);
}

事件注册和事件触发

registerEvent("someEventName", foo);
registerEvent("anotherEventName", bar);

fireEvent("someEventName", 42);
fireEvent("anotherEventName", true, "Hello World");

下面是指向打字稿 playground 的链接


上面的实现不是类型安全的。您不会得到任何有关回调期望的参数的提示。fireEvent

所以我尝试使用打字稿参数实用程序类型:

const fireEvent = (name: string, ...args: Parameters<typeof eventRegistry.get(name)>): void => {
    eventRegistry.get(name)!(...args);
}

但这让我无处可去。要正确键入 args,我首先需要将回调分配给事件,但我不知道如何引用名称。

typescript 函数签名

评论

0赞 Tobias S. 7/18/2022
您期望在编译时自动完成参数。但是,哪些事件注册到哪个事件目前仅在运行时已知。您应该创建一个编译时已知数据结构,其中包含有关特定事件和事件名称之间关系的信息。然后你可以用泛型解决这个问题。...argsname

答:

1赞 Titian Cernicova-Dragomir 7/18/2022 #1

您可以使用 for 获取函数的参数,也可以使用 spread 将参数传播回函数。您甚至可以使用泛型类型参数和索引类型从对象拼写错误中获取函数的类型:Parameters

type EventRegistry = {
    someEventName: typeof foo
    anotherEventName: typeof bar
}

const eventRegistry: Map<string, CallbackType> = new Map();
const fireEvent = <K extends keyof EventRegistry>(name: K, ...args: Parameters<EventRegistry[K]>): void => {
    eventRegistry.get(name)!(...args);
}

fireEvent("anotherEventName", true, "") // ✅
fireEvent("anotherEventName", true, 0) // ❌

游乐场链接

您在这里的真正问题是,它不会以任何方式在 .这意味着不知道注册了什么事件,如果我们在编译时想要这些信息,我们需要将其从寄存器调用流向调用。registerEventfireEventfireEventfireEvent

这是我对它的刺痛,不是一个明确的解决方案,而是一个起点:

class EventRegistry<T extends  Record<string, CallbackType> = {}>{
    eventRegistry: Map<string, CallbackType> = new Map();

    registerEvent<K extends string, C extends CallbackType>(name: K, callback: C):EventRegistry<T & Record<K, C>>   {
        this.eventRegistry.set(name, callback);
        return this;
    }

    fireEvent<K extends keyof T & string>(name: K, ...args: Parameters<T[K]>): void  {
        this.eventRegistry.get(name)!(...args);
    
}
const reg = new EventRegistry()
    .registerEvent("someEventName", foo)
    .registerEvent("anotherEventName", bar);

reg.fireEvent("someEventName", 42); // ✅
reg.fireEvent("anotherEventName", true, "Hello World"); // ✅
reg.fireEvent("anotherEventName", true, 0); // ❌

游乐场链接