考虑以下打字稿代码:
type CtorType<T> = {
new(...args: any[]): T;
}
type IWrapper<T> = {
value: T;
}
function foo<T>(ctor: CtorType<T>): IWrapper<T> {
return {
value: new ctor('foo')
};
}
class A { }
class B<T> { }
const a = foo(A);
const b = foo(B<number>);
奇怪的是,TS编译器在最后一行抛出错误:
TS2348: Value of type 'typeof B<number>' is not callable. Did you mean to include 'new'?
在操场上,我还看到TS将foo(A)
编译为foo(A)
,但是将foo(B<number>)
编译为foo(B())
,这更加令人惊讶。
为什么会这样?是否有一种解决方法可以使其使用B
并维护type参数?
答案 0 :(得分:1)
我正在更改您的示例代码,以停止奇怪的问题,例如类型A
,B<string>
和B<number>
都相同,并且不调用构造函数A
或他们不接受带有B
参数的"foo"
。
即使在示例代码中,拥有empty classes或unused type parameters也是一个坏主意,因为它们通常会导致意外的结果。
并且由于您的CTorType<T>
使用了(...args: any[])
参数列表,因此它忘记了对构造函数参数的任何约束,并允许进行不安全的调用。当foo()
的实现最终以两个A
作为参数调用B
和"foo"
构造函数时,构造函数都不接受任何参数。因此,我将使用一个空的参数列表()
,仅在new ctor()
内部调用foo()
。
赞:
// zero arg constructor to prevent unsafe calls
type CtorType<T> = {
new (): T;
};
type IWrapper<T> = {
value: T;
};
function foo<T>(ctor: CtorType<T>): IWrapper<T> {
return {
value: new ctor() // zero arg call
};
}
// make A not empty
class A {
a = "a";
}
// make B not empty and depend on T
class B<T> {
b: T | undefined = undefined;
}
这只是为了将示例代码放到一个地方,唯一的问题就是您要解决的问题。
好的,继续:
B<number>
是语法错误;编译器将其解释为具有指定类型参数的泛型函数调用的第一部分,例如在B<number>(13)
中,假设B
与function B<T>(x: T){}
类似。但是B
不是可调用函数,它是构造函数,因此您会收到该错误。如果可以通话,则会提示您错过了通话(即"(" expected
)。有一个名为B<number>
的类型,但没有一个名为B<number>
的 value (在TypeScript中为values are not generic)。
没有简短的语法可以在不调用或new
的情况下在函数或构造函数中指定通用参数。您可以做的一件事是将构造函数从一个泛型(可以作用于type参数的所有值)扩展到一个具体的(只能作用于type参数的单个值)。 ,就像这样:
const BNumber: new () => B<number> = B;
const b = foo(BNumber); // IWrapper<B<number>>
第一个BNumber
起作用是因为B
实际上是new () => B<number>
(以及new () => B<string>
和new () => B<Array<{a: string}>>
等)。然后,当您在foo
上调用BNumber
时,它将根据需要返回一个IWrapper<B<number>>
。
如果您经常使用B
做这种事情,则可以将其扩展为您要调用的函数,如下所示:
const BWidener = <T>(): new () => B<T> => B;
const bAlso = foo(BWidener<number>()); // IWrapper<B<number>>
const bString = foo(BWidener<string>()); // IWrapper<B<string>>
const bWhatever = foo(BWidener<Array<{ a: string }>>()); // IWrapper<B<{a: string;}[]>>
或者,如果这只是您要做的一次,并且不介意使用type assertions,那么您可以在一行中这样做:
const bAsWell = foo(B as new () => B<number>); // IWrapper<B<number>>
好的,希望能有所帮助。祝你好运!