簡體   English   中英

將類型數組轉換為 typescript 中的函數參數類型

[英]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[] ,並且EventReceiverIEmitter.emit()在 rest/spread 位置使用元組來允許多個參數的函數工作。 您必須稍微更改您的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兼容,因此我使用了類型斷言

現在你應該能夠按照你想要的方式調用事物了:

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]> 如果重要,我已將其包含在下面的操場鏈接中。


如果這些解決方案中的任何一個對您不起作用,請詳細說明...最好通過在代碼中添加一些東西來演示問題。 無論如何,希望對您有所幫助並祝您好運!

Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM