Typescript 为什么通用约束会产生TS2417错误?
我在实现以下代码的Table类上遇到TypeScript 2417错误:Typescript 为什么通用约束会产生TS2417错误?,typescript,Typescript,我在实现以下代码的Table类上遇到TypeScript 2417错误: 导出抽象类基表{ 受保护的构造函数(){} 静态生成器=类{ 受保护的构造函数(){} build():T{ 返回这个.buildTable(); } 受保护的buildTable():T{ 抛出新错误(“必须在子类中实现”); } }; } 导出类表扩展了BaseTable{ 私有构造函数(){ 超级(); } 静态生成器=类扩展了BaseTable.Builder{ 受保护的构造函数(){ 超级(); } 静态创建()
导出抽象类基表{
受保护的构造函数(){}
静态生成器=类{
受保护的构造函数(){}
build():T{
返回这个.buildTable();
}
受保护的buildTable():T{
抛出新错误(“必须在子类中实现”);
}
};
}
导出类表扩展了BaseTable{
私有构造函数(){
超级();
}
静态生成器=类扩展了BaseTable.Builder{
受保护的构造函数(){
超级();
}
静态创建(){
返回新的Table.Builder();
}
受保护的buildTable():表{
返回新表();
}
};
}
这会产生错误
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)
我的目标是拥有私有或受保护的构造函数,并且能够分两步调用代码
constbuilder=Table.builder.create();
const table=builder.build();
我认为这可能是由于Builder
类是静态的,所以我尝试了以下方法:
导出抽象类基表{
构造函数(){}
}
导出命名空间基表{
导出抽象类生成器{
受保护的构造函数(){}
build():T{
返回这个.buildTable();
}
受保护的抽象构建表():T;
}
}
导出类表扩展了BaseTable{
构造函数(){
超级();
}
}
导出命名空间表{
导出类生成器扩展了BaseTable.Builder{
受保护的构造函数(){
超级();
}
静态创建(){
返回新的Table.Builder();
}
受保护的buildTable():表{
返回新表();
}
}
}
这段代码会产生相同的错误,并破坏了使用私有构造函数的目的
有人能给我解释一下这个错误以及如何实现这个代码吗。问题是
BaseTable.Builder
被声明为一个泛型类,可以充当消费者想要的任何T扩展BaseTable
。无论谁调用new BaseTable.Builder()
(由于构造函数受protected
),我都不清楚该调用谁)都可以指定T
,比如new BaseTable.Builder()
为了扩展基表
,类的静态端和实例端都需要分别分配给基表
的静态端和实例端。即既有接口端继承,也有静态端继承;有关静态继承是否可取的讨论,请参阅。但在可预见的未来,情况就是这样
因此Table.Builder
必须可分配给BaseTable.Builder
。但事实并非如此。虽然Table.Builder
可以(由谁?)用来创建调用方想要的build()
任何类型的T扩展Basetable
,但是Basetable.Builder
只能(由谁?)用来创建build()
aTable
的新事物
如果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;
现在没有错误(除了可能的声明警告和其他private
/受保护的
东西在夜间颠簸之外),因为Table.Builder
是BaseTable.Builder
的真正子类型
我知道我可以从中删除泛型,但这意味着我必须将表实例转换为正确的类型。我也可以用香草JavaScript编写它。这段代码按原样编译和运行,并完全按照我希望和期望的方式执行。当我检查原型链时,它看起来正是我期望的样子。在一个完美的世界里,TypeScript会让我抑制这个错误,因为它对我的实现并不重要。这里的问题是关于TypeScript的,所以我试图找出是否有一种方法可以使用泛型来编写它,从而使TypeScript感到高兴。“我必须将表实例转换为正确的类型”。真正地演示一下。如果您只是想抑制错误,可以使用like。但是这个错误是有原因的,那就是您编写的
Table
没有正确扩展BaseTable
。如果您的表
类型正确,则基表
错误,需要更改。这与我能得到的“有人能向我解释这个错误以及如何实现这个代码”非常接近。如果你有更好的答案,请告诉我。干杯我不知道/@ts ignore
。从你的回答中,我对这个问题有了更多的理解,但这并不是我想要的。您反复地说表
类没有正确地扩展基表
,但您没有说如何扩展。我通过向BaseTable.Builder
添加一个静态创建函数成功地消除了这个错误,尽管它永远不会被调用,但它只有在tsconfig中关闭noImplicitAny
时才起作用,因为我还不知道如何键入它。我想知道如果我能将BaseTable.Builder
抽象化和静态化,这是否会成为一个问题“你反复地说Table
类没有正确地扩展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();
}
};
}