为具有 none 或许多不同参数类型的函数定义 Typescript 类型

Define a Typescript type for a function with none or many different parameter types

我有一个将传递的函数绑定到 HTMLElement 的函数:

  global.d.ts:
  type tPrimitivRestParams = (number | object | string)[];
  type tDefaultVoidFunction = (...params: tPrimitivRestParams) => void;
  export default class AwesomeFunctions {
    public static addClickCallback(id: HTMLElement, func: tDefaultVoidFunction, scope: object, ...argArray: tPrimitivRestParams): void {
      [...] // checks
      // eslint-disable-next-line no-null/no-null
      element.onclick = func ? func.bind(scope, ...argArray) : null;
    }
  }
  export default class AwesomeClass {
    private bindMe(id: string):void {}
    private bindMeToo():void {}
    private bindMeThree(a:string, b: number, c: object): void {}
    private bindMeFour(clazz: SomeClass): void {}

    private binder():void {
      AwesomeFunctions.addClickCallback(document.getElementById('...'), this.bindMeToo, this); // TS says yes
      AwesomeFunctions.addClickCallback(document.getElementById('...'), this.bindMe, this, 'param'); // TS says no
      AwesomeFunctions.addClickCallback(document.getElementById('...'), this.bindMeThree, this, 'param', 2, {whoopi: 'goldberg', age: 24, 'actual-age': 'dontcha dare askn!!!'}); // TS says no
      AwesomeFunctions.addClickCallback(document.getElementById('...'), this.bindMeThree, this, new SomeClass()); // TS says no
    }
  }

它在 JS 上运行良好,但 TS 对除第一个以外的所有人大喊大叫:

// Example for bindMe
S2345: Argument of type '(id: string) => void' is not assignable to parameter of type 'tDefaultVoidFunction'.

如果我这样做:

AwesomeFunctions.addClickCallback(..., this.bindMe as tDefaultVoidFunction , ...);

TS满意,但我觉得不对

我知道这是 tDefaultVoidFunction 类型的问题。是否可以定义一个类型以使 TS 对所有绑定都满意?我不能使用 any、null 或 Function。

附带问题:

我也可以这样做:

AwesomeFunctions.addClickCallback(..., () => this.bindMe('param'), this);

TS 也很满意,但 95% 的 addClickCallback 函数变得毫无意义。但这并不是我犹豫是否使用箭头函数的原因。我担心范围可能会有所不同。据我所知,作用域始终是 this。所以在这种情况下,我声称箭头函数和绑定的行为相同。我错了吗?


可重现的例子:

type tPrimitivRestParams = (number | object | string)[];
type tDefaultVoidFunction = (...params: tPrimitivRestParams) => void;

class AwesomeFunctions {
    public static addClickCallback(id: string, func: tDefaultVoidFunction, scope: object, ...argArray: tPrimitivRestParams): void {
        // DO something
    }
}

class AwesomeClass {
    private bindMe(id: string):void {}
    private bindMeToo():void {}
    private bindMeThree(a:string, b: number, c: object): void {}

    private binder():void {
        AwesomeFunctions.addClickCallback('A', this.bindMeToo, this);
        AwesomeFunctions.addClickCallback('B', this.bindMe, this, 'param');
        AwesomeFunctions.addClickCallback('C', this.bindMeThree, this, 'param', 2, {whoopi: 'goldberg', age: 24, 'actual-age': 'dontcha dare askn!!!'});
    }
}

游乐场:TS Example

这就是你告诉编译器的内容:

type tDefaultVoidFunction = (...params: (number | object | string)[]) => void;

回调必须接受任意数量的参数,其中每个参数必须接受数量 |对象 |字符串。

让我们看看你的函数...

const example: tDefaultVoidFunction = (id: string): void {}

该参数只接受字符串,因此编译器会告诉您这是错误的。 (缺少数字和对象)

您可以使用联合类型定义多个允许的签名:

type tDefaultVoidFunction = ((id: string) => void) 
    | (() => void) 
    | ((a: string, b: number, c: object) => void) 
    | ((clazz: SomeClass) => void);

备注:

根据评论更新

可能的选项:

而不是写作:

AwesomeFunctions.addClickCallback(document.getElementById('...'), this.bindMeToo, this);

使用:

document.getElementById('...')?.addEventListener('click', (e) => this.bindMeTo());
// ignoring that we do not provide valid parameters in both cases