我在实现以下代码的 Table 类上收到 TypeScript 2417 错误:
export abstract class BaseTable {
protected constructor() {}
static Builder = class<T extends BaseTable> {
protected constructor() {}
build(): T {
return this.buildTable();
}
protected buildTable(): T {
throw new Error("Must be implemented in subclasses");
}
};
}
export class Table extends BaseTable {
private constructor() {
super();
}
static Builder = class extends BaseTable.Builder<Table> {
protected constructor() {
super();
}
static create() {
return new Table.Builder();
}
protected buildTable(): Table {
return new Table();
}
};
}
这会产生错误
Class static side 'typeof Table' incorrectly extends base class static side 'typeof BaseTable'.
The types returned by '(new Builder()).build()' are incompatible between these types.
Type 'Table' is not assignable to type 'T'.
'Table' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'BaseTable'.(2417)
我的目标是拥有私有或受保护的构造函数,并且能够分两步调用代码
const builder = Table.Builder.create();
const table = builder.build();
我认为这可能是由于 Builder
类是静态的,所以我尝试了这个:
export abstract class BaseTable {
constructor() {}
}
export namespace BaseTable {
export abstract class Builder<T extends BaseTable> {
protected constructor() {}
build(): T {
return this.buildTable();
}
protected abstract buildTable(): T;
}
}
export class Table extends BaseTable {
constructor() {
super();
}
}
export namespace Table {
export class Builder extends BaseTable.Builder<Table> {
protected constructor() {
super();
}
static create() {
return new Table.Builder();
}
protected buildTable(): Table {
return new Table();
}
}
}
这段代码产生了同样的错误并且违背了拥有私有构造函数的目的。
谁能向我解释这个错误以及如何实现这段代码。
答案 0 :(得分:1)
问题在于 BaseTable.Builder
被声明为一个泛型类,可以充当消费者想要的任何 T extends BaseTable
。谁可以调用 new BaseTable.Builder()
(我不清楚那可能是谁,因为构造函数是 protected
))可以指定 T
,例如 new BaseTable.Builder<{foo: string}>()
。
为了扩展 BaseTable
,类的静态端和实例端都需要分别分配给 BaseTable
的静态端和实例端。即有接口端和静态端继承;有关是否需要静态继承的一些讨论,请参见 microsoft/TypeScript#4628。不过,在可预见的未来,事情就是这样。
所以 Table.Builder
必须可分配给 BaseTable.Builder
。但事实并非如此。 Table.Builder
可用于(由谁?)来创建build()
调用者想要的任何类型T extends Basetable
的新事物,BaseTable.Builder
只能用于(同样,由谁?) 创造build()
Table
的新事物。
如果 Table
正确扩展 BaseTable
,并且如果我们去掉隐私/保护修饰符以便有人可以实际演示打字问题(并忽略构造签名),则问题是:>
const hmm = new BaseTable.Builder<{ foo: string }>().build().foo; // okay
const AlsoBaseTable: typeof BaseTable = Table;
const uhOh = new AlsoBaseTable.Builder<{ foo: string }>().build().foo;
您应该被允许在很大程度上将 Table
类构造函数视为它与 BaseTable
的类构造函数具有相同的静态属性,如果您的 Table.Builder
实现,这将是一个问题与 BaseTable
的通用方式不同。
所以,退一步来说,我认为您并不真的希望 BaseTable.Builder
成为泛型,或者至少不像 TypeScript 的泛型所暗示的那样泛型。 TypeScript 的泛型通用而不是存在(见this Q/A for more info),所以当你说class <T extends BaseTable>{...}
时,你是说这适用于所有人 T
的规范,而不仅仅是 T
的某些规范。
相反,您可能想要做的是扩大 BaseTable.Builder
以便它只声称用 BaseTable
做事情,然后让子类按照他们认为合适的方式缩小。这不一定会强制执行您希望强制执行的所有约束,但只要您正确实施子类,事情应该会解决:
abstract class BaseTable {
protected constructor() { }
static Builder = class {
protected constructor() { }
build(): BaseTable {
return this.buildTable();
}
protected buildTable(): BaseTable {
throw new Error("Must be implemented in subclasses");
}
};
}
class Table extends BaseTable {
private constructor() {
super();
}
static Builder = class extends BaseTable.Builder {
protected constructor() {
super();
}
static create() {
return new Table.Builder();
}
protected buildTable(): Table {
return new Table();
}
};
}
现在没有错误(除了可能的声明警告和其他 private
/protected
事情会在夜间颠簸),因为 Table.Builder
是 {{1} 的真正子类型}.