我想要一个函数,该函数返回一个新函数,其类型取决于传递给包装函数的参数。包装函数应将函数和对象作为参数。这是一些类型信息:
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
我目前所拥有的存在一些问题。一种似乎我丢失了id
中myHandler
的所有类型信息...任何字符串都传递而没有错误。参数也一样。
我真的不确定问题是什么,因为类型信息似乎很有意义?
此外,我希望事件映射既可以是返回通用Event
的函数,也可以是返回通用Event
的函数(换句话说就是静态事件)... { {1}} ...但是如果我不能这样做,我很好。
答案 0 :(得分:1)
我认为您缺少的是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
键入为'foo'
而不是string
)。如果您使用的是TS3.4 +,则可以使用const
assertion:
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
希望有所帮助;祝你好运!
答案 1 :(得分:0)
扩展@jcalz的答案以包括我提出的其他要求,即EventMap
中的属性可以是返回Event
的函数,也可以是静态的Event
。这似乎有点棘手,但效果很好。这是EventMap
的新类型信息:
type EventMap<T extends Event> = {
[id: string]: T | ((...args: any) => T);
};
我向myEventMap
添加了一个附加属性:
const myEventMap = {
hello: (p: string) => ({ type: 'foo', fizz: p } as const),
goodbye: (n: number) => ({ type: 'bar', buzz: n } as const),
bonjour: { type: 'foo', fizz: 'something' } as const,
};
如果类型是函数,这需要我编写一个辅助类型来提取参数:
type HandlerParams<T> = T extends ((...args: any[]) => any) ? Parameters<T> : Array<undefined>;
,因此重新实现的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: HandlerParams<M[P]>) => {
const eventProp = eventMap[id];
let event: T;
if (typeof eventProp === 'function') {
event = (eventMap[id] as ((...args: any) => T))(...params);
} else {
event = eventProp as any; // couldn't get this working any other way, aka the hack
}
executor(event);
};
}