将类型数组转换为打字稿中的函数参数类型
Transform array of types into functions parameters types in typescript
我在 typescript 中有一个自定义事件发射器系统,但我只能使用 1 个参数,代码如下:
type EventMap = Record<string, any>;
type EventKey<T extends EventMap> = string & keyof T;
type EventReceiver<T> = (params: T) => void;
interface IEmitter<T extends EventMap> {
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
emit<K extends EventKey<T>>(eventName: K, params: T[K]): void;
}
class EventHandler<T extends EventMap> implements IEmitter<T> {
private emitter: EventEmitter = new EventEmitter();
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
this.emitter.on(eventName, fn);
}
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
this.emitter.off(eventName, fn);
}
emit<K extends EventKey<T>>(eventName: K, params: T[K]): void {
this.emitter.emit(eventName, params);
}
}
const handler = new EventHandler<{
foo: string,
bar: number
multiple: [ string, number ]
}>();
handler.emit('foo', 'baz');
handler.emit('multiple', 'aaa', 10);
handler.on('foo', (param) => console.log(param)); // baz
handler.on('multiple', (param) => console.log(param)); // [ 'aaa', 10 ] => This is not what I want
你可以看到multiple
事件为on
方法接受了一个参数,这个参数是一个包含两个值的数组。
但是我希望能够做到这一点:
handler.on('multiple', (aaa, ten) => console.log(`${aaa} , ${ten}`)); // aaa , 10
如何修改代码才能执行此操作?
所以,我在这里的第一个倾向是将所有参数都变成元组类型的参数列表,所以string
类型的单个参数写成[string]
。可以对您的代码进行以下更改:
type EventMap = Record<string, any[]>;
type EventReceiver<T extends any[]> = (...params: T) => void;
interface IEmitter<T extends EventMap> {
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
emit<K extends EventKey<T>>(eventName: K, ...params: T[K]): void; // note
}
你可以看到 EventMap
现在要求值的类型是 any[]
,并且 EventReceiver
和 IEmitter.emit()
都使用 tuples in rest/spread positions 来允许函数多个参数的工作。您必须稍微更改 EventHandler
class 以匹配:
class EventHandler<T extends EventMap> implements IEmitter<T> {
private emitter: EventEmitter = new EventEmitter();
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
this.emitter.on(eventName, fn as EventReceiver<any[]>);
}
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
this.emitter.off(eventName, fn as EventReceiver<any[]>);
}
emit<K extends EventKey<T>>(eventName: K, ...params: T[K]): void {
this.emitter.emit(eventName, ...params);
}
}
(另外:请注意 EventReceiver<T[K]>
不被视为与 (...args: any[])=>void
兼容,所以我使用了 type assertion)
现在您应该可以按照自己的方式调用事物了:
const handler = new EventHandler<{
foo: [string],
bar: [number],
multiple: [string, number]
}>();
handler.emit('foo', 'baz');
handler.emit('multiple', 'aaa', 10);
handler.on('foo', (param) => console.log(param));
handler.on('multiple', (aaa, ten) => console.log(`${aaa} , ${ten}`));
这里的替代方案可能是让 EventReceiver
检查 T
是否是数组类型,如果是则传播它,但这很笨拙,除非你需要它,否则我会小心尝试它。如果需要,它可能会使用类似
type MaybeSpread<T> = T extends readonly any[] ? T : [T];
type EventReceiver<T> = (...params: MaybeSpread<T>) => void;
然后是...params: MaybeSpread<T[K]>
。如果重要的话,我已将其包含在下面的操场 link 中。
如果这些解决方案中的任何一个对您不起作用,请详细说明...最好在代码中添加一些内容来演示问题。无论如何,希望对您有所帮助,祝您好运!
我在 typescript 中有一个自定义事件发射器系统,但我只能使用 1 个参数,代码如下:
type EventMap = Record<string, any>;
type EventKey<T extends EventMap> = string & keyof T;
type EventReceiver<T> = (params: T) => void;
interface IEmitter<T extends EventMap> {
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
emit<K extends EventKey<T>>(eventName: K, params: T[K]): void;
}
class EventHandler<T extends EventMap> implements IEmitter<T> {
private emitter: EventEmitter = new EventEmitter();
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
this.emitter.on(eventName, fn);
}
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
this.emitter.off(eventName, fn);
}
emit<K extends EventKey<T>>(eventName: K, params: T[K]): void {
this.emitter.emit(eventName, params);
}
}
const handler = new EventHandler<{
foo: string,
bar: number
multiple: [ string, number ]
}>();
handler.emit('foo', 'baz');
handler.emit('multiple', 'aaa', 10);
handler.on('foo', (param) => console.log(param)); // baz
handler.on('multiple', (param) => console.log(param)); // [ 'aaa', 10 ] => This is not what I want
你可以看到multiple
事件为on
方法接受了一个参数,这个参数是一个包含两个值的数组。
但是我希望能够做到这一点:
handler.on('multiple', (aaa, ten) => console.log(`${aaa} , ${ten}`)); // aaa , 10
如何修改代码才能执行此操作?
所以,我在这里的第一个倾向是将所有参数都变成元组类型的参数列表,所以string
类型的单个参数写成[string]
。可以对您的代码进行以下更改:
type EventMap = Record<string, any[]>;
type EventReceiver<T extends any[]> = (...params: T) => void;
interface IEmitter<T extends EventMap> {
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
emit<K extends EventKey<T>>(eventName: K, ...params: T[K]): void; // note
}
你可以看到 EventMap
现在要求值的类型是 any[]
,并且 EventReceiver
和 IEmitter.emit()
都使用 tuples in rest/spread positions 来允许函数多个参数的工作。您必须稍微更改 EventHandler
class 以匹配:
class EventHandler<T extends EventMap> implements IEmitter<T> {
private emitter: EventEmitter = new EventEmitter();
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
this.emitter.on(eventName, fn as EventReceiver<any[]>);
}
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
this.emitter.off(eventName, fn as EventReceiver<any[]>);
}
emit<K extends EventKey<T>>(eventName: K, ...params: T[K]): void {
this.emitter.emit(eventName, ...params);
}
}
(另外:请注意 EventReceiver<T[K]>
不被视为与 (...args: any[])=>void
兼容,所以我使用了 type assertion)
现在您应该可以按照自己的方式调用事物了:
const handler = new EventHandler<{
foo: [string],
bar: [number],
multiple: [string, number]
}>();
handler.emit('foo', 'baz');
handler.emit('multiple', 'aaa', 10);
handler.on('foo', (param) => console.log(param));
handler.on('multiple', (aaa, ten) => console.log(`${aaa} , ${ten}`));
这里的替代方案可能是让 EventReceiver
检查 T
是否是数组类型,如果是则传播它,但这很笨拙,除非你需要它,否则我会小心尝试它。如果需要,它可能会使用类似
type MaybeSpread<T> = T extends readonly any[] ? T : [T];
type EventReceiver<T> = (...params: MaybeSpread<T>) => void;
然后是...params: MaybeSpread<T[K]>
。如果重要的话,我已将其包含在下面的操场 link 中。
如果这些解决方案中的任何一个对您不起作用,请详细说明...最好在代码中添加一些内容来演示问题。无论如何,希望对您有所帮助,祝您好运!