如何获取派生类方法参数的类型?

问题描述 投票:0回答:1

我想创建一个将 自定义 HTML 元素与一些附加属性和方法混合在一起的类:如果我扩展该类以创建新组件,则新组件将是具有附加属性/方法的自定义 HTML 元素。

我尝试添加的一个功能是定义和分派自定义事件的帮助程序。理想情况下,每个组件都会有一个

events
对象来定义每个事件,包括:

  1. 活动名称
  2. 随事件传递的数据的“形状”
  3. 随事件传递的数据应发生的任何转换

然后,当发生某些交互时,我们可以

emit
事件并使用正确的数据分派它。

例如,假设我们正在创建一个店面,人们可以在其中购买烘焙食品:

export function ComponentFactory(BaseHTMLElement: typeof HTMLElement) {
    return class Component extends BaseHTMLElement {
        events = {} as Record<string, (...args: Array<any>) => any>; // eslint-disable-line @typescript-eslint/no-explicit-any

        emit<
            EventKey extends keyof this[`events`],
            EventDetail extends this[`events`][EventKey],
        >(eventKey: EventKey, ...args: Parameters<EventDetail>) {
            const eventName = eventKey as string;
            const getDetail = this.events[eventName] as EventDetail;
            const detail = getDetail(...args) as ReturnType<EventDetail>;
            this.dispatchEvent(new CustomEvent(eventName, {
                bubbles: true,
                detail,
            }));
        }
    };
}

export class BakeryItem extends ComponentFactory(HTMLDivElement) {
    events = {
        buy: (quantity: number) => quantity,
        buyDozen: (dozens: number) => dozens * 12,
    };

    onBuyClick(quantity: number) {
        this.emit(`buy`, quantity);
    }

    onBuyDozenClick(quantity: number) {
        this.emit(`buyDozen`, quantity);
    }
}

Typescript 在调用

this.emit
时抛出错误。例如,调用
this.emit('buy', quantity)
时:

Argument of type '[number]' is not assignable to parameter of type 'Parameters<this["events"]["buy"]>'.

但是,当我调用

new BakeryItem().emit('buy', 3)
时,它会正确推断所有类型并且不会引发错误。

如何正确输入此内容以便可以同时调用

bakeryItem.emit
new BakeryItem().emit

javascript typescript web-component custom-element
1个回答
0
投票

设法让 ComponentFactory 接受泛型作为您的

events
对象的类型,然后使用它在发射函数中构造您的泛型类型来使其工作。当我在这里进行初始测试时,似乎
Parameters
泛型确实不喜欢纯数组访问中的值(例如
Parameters<(typeof this)["events"]["buy"]>
而不是
Parameters<typeof this.events.buy>
)。下面是我如何设法让它工作,但它确实需要添加一个参数来对您的
ComponentFactory
函数进行类型推断:

export function ComponentFactory<
  T extends Record<string, (...args: any[]) => any>
>(BaseHTMLElement: typeof HTMLElement, events: T) {
  return class Component extends BaseHTMLElement {
    events = events;

    emit<
      EventKey extends keyof typeof this.events,
      EventDetail extends (typeof this.events)[EventKey]
    >(eventKey: EventKey, ...args: Parameters<(typeof this.events)[EventKey]>) {
      const eventName = eventKey as string;
      const getDetail = this.events[eventName] as EventDetail;
      const detail = getDetail(...args) as ReturnType<EventDetail>;
      this.dispatchEvent(
        new CustomEvent(eventName, {
          bubbles: true,
          detail,
        })
      );
    }
  };
}

export class BakeryItem extends ComponentFactory(HTMLDivElement, {
  buy: (quantity: number) => quantity,
  buyDozen: (dozens: number) => dozens * 12,
}) {
  onBuyClick(quantity: number) {
    this.emit("buy", quantity);
  }

  onBuyDozenClick(quantity: number) {
    this.emit("buyDozen", quantity);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.