【问题标题】:Transform array of types into functions parameters types in typescript将类型数组转换为打字稿中的函数参数类型
【发布时间】:2020-06-30 03:13:56
【问题描述】:

我在 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

如何修改代码才能做到这一点?

【问题讨论】:

  • 重构一切以使用元组代替? type EventReceiver&lt;T extends readonly any[]&gt; = (...params: T) =&gt; void; 然后{foo: [string], bar: [number], multiple: [string, number]}?
  • 或使EventReceiver 复杂化以检测数组类型,但这可能会做您不想要的事情并且可能很脆弱:type EventReceiver&lt;T&gt; = (...params: T extends readonly any[] ? T : [T]) =&gt; void
  • 在 4.0 中也可以使用命名元组:multiple: [strarg: string, nrArgs: number]
  • 如果你想把这些想法写成答案,请告诉我,祝你好运
  • import {EventEmitter} from 'events';events是一个节点模块。

标签: typescript types eventemitter


【解决方案1】:

所以,我在这里的第一个倾向是将所有参数转换为元组类型的参数列表,因此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[],并且EventReceiverIEmitter.emit() 都使用tuples in rest/spread positions 以允许多个参数的函数工作。您必须稍微更改您的 EventHandler 类以匹配:

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&lt;T[K]&gt; 不被视为与(...args: any[])=&gt;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&lt;T[K]&gt;。如果重要,我已将其包含在下面的游乐场链接中。


如果这些解决方案中的任何一个对您都不起作用,请详细说明...最好通过在代码中添加一些内容来演示该问题。无论如何,希望对您有所帮助并祝您好运!

Playground link to code

【讨论】:

  • 谢谢,我试试这个!
猜你喜欢
  • 2022-01-21
  • 2022-10-14
  • 1970-01-01
  • 2022-11-11
  • 2019-07-05
  • 2020-03-22
  • 2019-03-20
  • 2022-01-12
  • 1970-01-01
相关资源
最近更新 更多