为没有或许多不同参数类型的函数定义 Typescript 类型
Posted
技术标签:
【中文标题】为没有或许多不同参数类型的函数定义 Typescript 类型【英文标题】:Define a Typescript type for a function with none or many different parameter types 【发布时间】:2021-12-31 11:50:46 【问题描述】:我有一个将传递的函数绑定到 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 对所有绑定感到满意?我不能使用任何、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
【问题讨论】:
请提供可重现的例子 @captain-yossarian 完成 【参考方案1】:这就是你告诉编译器的:
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);
注意事项:
private function bindMe(id: string):void
在类中没有有效的 TypeScript 代码,我想这只是一个错字。
文档:https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html
基于 cmets 更新
可能的选择:
而不是写:
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
【讨论】:
感谢您指出错字。很多 php 和 TS 之间的切换。 ;) 但我固执地解释了我的问题还不够。我知道TS抱怨的原因。但是我不能告诉 TS 我可能想通过的每一门课。那将是荒谬的。我有 40 多节课。这些函数也可以只接受一个数字、一个字符串或一个数组。你不能告诉我我必须告诉 TS 每种可能的类型。我会失去信心。在对象中,我们有类似 [key: string]: ... 我希望函数也有可能。 如果你想接受每个函数,答案是Function
,它经常被阻止,因为它不可靠。当您调用回调时,您传递参数并且可用参数定义允许的签名。您将如何确保始终使用正确的参数调用您的回调?在您的代码中,您绑定到onclick
,它将事件作为参数传递。这应该如何与需要字符串的函数一起使用?它不会得到字符串,而是PointerEvent
,所以它会失败。
听起来合乎逻辑。但是我宁愿像在我的问题示例中那样使用包装函数。它只是一个没有参数的函数,它使用我想绑定的参数调用函数。问题是,我将不得不向一些开发人员介绍情况,我希望我可以避免这种情况。或者还可以。也许还有另一种解决方案。
类和箭头函数为处理this
带来了更多“类PHP/C#/Java”风格的javascript。例如。 “箭头函数根据定义箭头函数的范围建立‘this’。” (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…) - 在这个世界上可能没有理由bind()
。例如。 (内部类方法)el.addEventListener('click', (e) => this.anotherClassMethod())
可能是一个选项。
包装函数是目前工作中的首选。我们只需要更改 100 多个 LOC。 :) 测试用例现在运行没有任何抱怨。我仍然必须弄清楚,如果类型 tDefaultVoidFunction 现在已过时。无论如何...感谢您的帮助。我将在我们完成更改代码的那一天接受答案。将在本周末结束。以上是关于为没有或许多不同参数类型的函数定义 Typescript 类型的主要内容,如果未能解决你的问题,请参考以下文章