考虑下面的代码,我创建了一个类,该类具有一个构造函数,该构造函数接收具有通用类型的参数,该参数用于定义第二个参数接收的参数的类型,在以下情况下,回调函数的参数为MouseEvent,因为第一个参数的值:
class MyClass<T extends keyof HTMLElementEventMap>{
constructor(tagName: T, lister: (ev: HTMLElementEventMap[T]) => any) { }
}
new MyClass("click", ev => { })
// ^^ (parameter) ev: MouseEvent
但是,如果我创建一个函数,其其余参数的类型为ConstructorParameters <typeof MyClass>,则第二个参数将解析为回调参数的MouseEvent类型。 Event | UIEvent | AnimationEvent | MouseEvent | FocusEvent | DragEvent | ErrorEvent | PointerEvent | ... 6 more ... | ClipboardEvent
function myFunction(...args: ConstructorParameters<typeof MyClass>) {}
myFunction("click", ev => { })
// ^^ (parameter) ev: Event | UIEvent | AnimationEvent | MouseEvent | FocusEvent | DragEvent | ErrorEvent | PointerEvent | ... 6 more ... | ClipboardEvent
如何在不重写myFunction
函数类型的情况下为回调参数获取正确的类型?
是否可以?
您想要说的是“采用任何构造函数类型并将其转换为接受相同参数的函数类型”。
一个问题是TypeScript的类型系统没有一种很好的方式来表达适用于泛型函数的“可调用或可构造对象的参数” 。如果您有类似的功能
function foo<T>(x: T, y: T): void { }
并尝试获取其参数列表,则通用参数T
将替换为其约束。在这种情况下,T
不受约束,因此具有以下隐式约束unknown
:
type FooParams = Parameters<typeof foo>;
// type FooParams = [x: unknown, y: unknown]
TypeScript没有正确的通用类型来表示通用函数的参数列表。通用函数在调用签名上具有其通用类型参数。但是像这样的元组类型[x: unknown, y: unknown]
没有调用签名,并且不能采用泛型类型参数:
// Not valid TS, do not use this:
type FooParams = <T>[x: T, y: T];
为了表示这一点,TypeScript将需要像microsoft / TypeScript#17574中所要求的任意通用值类型之类的东西,但是它没有这些。
好的,所以不用担心元组类型,也许我们可以自动将一种泛型函数类型转换为另一种。再次不幸的是,该语言没有正确的类型操作符来执行此操作。为了捕获泛型函数及其类型参数之间的关系,TypeScript可能需要像microsoft / TypeScript#1213所要求的“高级类型”之类的东西,但是它也不具有这些。
在TypeScript 3.4之前,我会说我们将完全陷入困境。但是Typescript 3.4引入了对泛型函数更高阶类型推断的支持。这是更高种类的类型的部分实现,但是没有可用的类型级语法。您可以将实际的通用函数值转换为另一个通用函数值,其中输出函数的类型与输入函数的类型完全相关。如果您没有实际的函数值,则可以制作一个或假装制作一个,对该值进行更高阶的推断,然后获取输出函数的类型。但是,正如您所指出的那样,如果您只关心键入,那么它将在您的输出中添加一些根本不需要的实际JavaScript。对于MyClass
示例,它看起来像这样:
// abuse TS3.4 support for higher order inference from generic functions
const ctorArgs = <A extends any[], R>(f: new (...a: A) => R): (...a: A) => void => null!
const myFunc = ctorArgs(MyClass)
type MyFunctionType = typeof myFunc;
const myFunction: MyFunctionType = (...args) => {}
myFunction("click", ev => { ev.buttons })
您可以看到它ctorArgs
正在执行所需的类型操作,但是在值级别。在上面的代码中,ctorArgs
正在丢弃其输入而只是假装返回一个函数;反过来,我们将丢弃输出,只是获取其类型。
由于您无法逃脱JavaScript的这一部分,因此也许您可以实际使用它来发挥自己的优势并进行myFunction
通用实现。您将要执行的操作将需要一个构造函数并将其转换为返回void的函数?调用new
它,然后丢弃结果?像这样:
const makeMyFunction = <A extends any[], R>(
f: new (...a: A) => R): (...a: A) => void =>
(...a) => { new f(...a) } // whatever the implementation should do
const myFunction = makeMyFunction(MyClass);
在这种情况下,myFunction
免费提供。但这是否适用取决于您的用例,我不知道。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句