首页 > 解决方案 > 来自对象属性的动态 Typescript 函数参数

问题描述

我想要一个函数,它返回一个新函数,其类型取决于传递给包装函数的参数。包装函数应该将函数和对象作为参数。以下是一些类型信息:

type Event = {};

type EventExecutor<T extends Event> = (event: T) => void;

type EventMap<T extends Event> = {
    [id: string]: (...args: any) => T;
};

并且该功能将实现如下:

function createEventHandler<
    T extends Event,
    M extends EventMap<T>,
    K extends keyof M
>(executor: EventExecutor<T>, eventMap: M) {
    return (id: K, ...params: Parameters<M[K]>) => {
        const event = eventMap[id](...params);
        executor(event);
    };
}

我期望它的工作方式是:

type MyEventA = { type: 'foo', fizz: string };
type MyEventB = { type: 'bar', buzz: number };

type MyEvent = MyEventA | MyEventB;

function myEventExecutor(event: MyEvent) {
    // ...
}

const myEventMap = {
    hello: (p: string) => ({ type: 'foo', fizz: p }),
    goodbye: (n: number) => ({ type: 'bar', buzz: n }),
};

const myHandler = createEventHandler(myEventExecutor, myEventMap);

myHandler('hello', 'world'); // correct
myHandler('goodbye', 42); // correct
myHandler('hello', 42); // ERROR
myHandler('goodbye', 'world'); // ERROR
myHandler('wrong', 'stuff'); // ERROR

我目前有一些问题。一个似乎我丢失了idin的所有类型信息myHandler......任何字符串都没有错误地传递。参数也一样。

我真的不确定问题是什么,因为类型信息似乎有意义???

此外,我希望能够让事件映射成为返回泛型的函数Event或只是那个泛型Event(换句话说,一个静态事件)Event | (...args: any) => Event......但如果我不能要做到这一点。

标签: typescript

解决方案


我认为您缺少的是返回值createEventHandler()本身应该是一个通用函数,如下所示:

function createEventHandler<
    T extends Event,
    M extends EventMap<T>,
    K extends keyof M
>(executor: EventExecutor<T>, eventMap: M) {
    return <P extends K>(id: P, ...params: Parameters<M[P]>) => {
        const event = eventMap[id](...params);
        executor(event);
    };

}

然后,您还需要确保您的类型myEventMap尽可能窄(以便type键入 as'foo'而不是 as string)。如果您使用的是 TS3.4+,则可以使用const断言

const myEventMap = {
    hello: (p: string) => ({ type: 'foo', fizz: p } as const),
    goodbye: (n: number) => ({ type: 'bar', buzz: n } as const),
};

否则,您可以通过多种方式解决它(例如{type: 'foo' as 'foo', fizz: p})。

鉴于这些更改,您将获得您正在寻找的行为:

myHandler('hello', 'world'); // correct
myHandler('goodbye', 42); // correct
myHandler('hello', 42); // ERROR
myHandler('goodbye', 'world'); // ERROR
myHandler('wrong', 'stuff'); // ERROR

希望有帮助;祝你好运!


推荐阅读