我正在尝试通过将createInstance与泛型一起使用来创建实例,如下所示。当我向函数传递两个参数时,它工作得很好。如果在没有第二个参数Ctor的情况下调用该函数,则它将默认提供BaseCtor。在这种情况下,它会引发类似“类型'BaseCtor'不可分配给类型'T'”的错误。我应该怎么做才能解决所有争论?
class BaseCtor {}
const createInstance = function createInstance<T extends BaseCtor>(
options: object = {},
Ctor: {new (): T} = BaseCtor
): T {
return new Ctor();
}
答案 0 :(得分:0)
您可能已经知道这一点,但是值得一遍:
泛型函数的类型参数由调用者而不是实现者解析。此外,只要传递的参数符合此要求,允许调用者使用尖括号中的显式类型手动指定此值。因此,以下所有方法都是调用createInstance()
的有效方法:
class Foo extends BaseCtor {
prop = "me";
}
const i1 = createInstance({ a: "okay" }, Foo); // Foo
const i2 = createInstance<Foo>({ a: "okay" }, Foo); // Foo
const i3 = createInstance({ a: "okay" }); // BaseCtor
const i4 = createInstance<Foo>({ a: "okay" }); // Foo ?
i4
会让您感到惊讶吗?在这种情况下,调用者已手动指定T
为Foo
,但仅传入一个参数。编译器将使用默认构造函数值BaseCtor
,但BaseCtor
不会构造Foo
个实例!这就是您所得到的错误的全部原因。就是说BaseCtor
不能保证可分配给T
,因为我们所知道的是T
扩展 BaseCtor
。
很显然,您需要某种方法,以便在仅使用一个参数时,调用方无法指定BaseCtor
的某些子类型。 function overload可能是实现此目的的最简单工具。提供两个不同的呼叫签名和一个实现,如下所示:
// call signature: two args
function createInstance<T extends BaseCtor>(
options: object,
Ctor: { new (): T }
): T;
// call signature: zero or one arg
function createInstance(options?: object): BaseCtor;
// implementation signature
function createInstance(
options: object = {},
Ctor: { new (): BaseCtor } = BaseCtor
): BaseCtor {
return new Ctor();
}
或等效地(更紧密地匹配您的代码):
const createInstance = function createInstance(
options: object = {},
Ctor: { new (): BaseCtor } = BaseCtor
): BaseCtor {
return new Ctor();
} as {
<T>(options: object, Ctor: new () => T): T;
(options?: object): BaseCtor;
};
现在i4
是不可能的,其余的呼叫将按照您希望的方式运行:
const i1 = createInstance({ a: "okay" }, Foo); // Foo
const i2 = createInstance<Foo>({ a: "okay" }, Foo); // Foo
const i3 = createInstance({ a: "okay" }); // BaseCtor
const i4 = createInstance<Foo>({ a: "okay" }); // error! expected 2 args, got 1
好的,希望能有所帮助。祝你好运!