Typescript TS2322:';T';可以使用与';无关的任意类型进行实例化;超类&x27;
我有一个基类,它有大量的子类Typescript TS2322:';T';可以使用与';无关的任意类型进行实例化;超类&x27;,typescript,Typescript,我有一个基类,它有大量的子类 class SuperClass{ constructor(){} } class SubClass1 extends SuperClass{ constructor(){super()} SubClass1Prop(){} } class SubClass2 extends SuperClass{ constructor(){super()} SubClass2Prop(){} } 我有一个包含不同子类的混合列表,我将其类
class SuperClass{
constructor(){}
}
class SubClass1 extends SuperClass{
constructor(){super()}
SubClass1Prop(){}
}
class SubClass2 extends SuperClass{
constructor(){super()}
SubClass2Prop(){}
}
我有一个包含不同子类的混合列表,我将其类型声明为超类[]
,因为它们共享相同的祖先
let list:SuperClass[] = [];
list.push(new SubClass1());
list.push(new SubClass2());
我有一个函数,它接受一个子类作为参数,然后过滤列表以返回该子类的实例
此函数的类型声明取自此SO answer(),但有一个小区别,即我返回的是一个列表,而不是单个对象
错误就在这里。我控制可以传递到filterList函数中的类型,并保证它始终是超类的子类。然而,TypeScript无法知道这一点。如何声明传递给此函数的任何类名必须始终是超类的子类,并且不能是“任意类型”?
function filterList<T>(className: { new():T }):T[] {
//TS2322: Type 'SuperClass[]' is not assignable to type 'T[]'.
// Type 'SuperClass' is not assignable to type 'T'.
// 'T' could be instantiated with an arbitrary type which could be unrelated to 'SuperClass'
return list.filter(obj => obj instanceof className);
}
虽然这非常简单,但您可以与和一起使用,以达到相同的效果:
function filterList<T extends { new (...args: any): any }>(className: T): InstanceType<T>[] {
const isT = (potentialT: any): potentialT is InstanceType<T> => {
return potentialT instanceof className;
}
return list.filter<InstanceType<T>>(isT);
}
函数过滤器列表(类名:T):InstanceType[]{
const isT=(potentialT:any):potentialT是InstanceType=>{
返回类名称的潜在实例;
}
返回列表。过滤器(isT);
}
列表
是类型超类[]
,因此过滤它将返回另一个超类[]
,因此函数返回类型超类[]
。但是,根据函数的类型,它实际上返回typeT[]
。如果T
满足SuperClass
,例如您所称的示例方式,那么祝您快乐。但是如果T
不满足超类
,那么返回的过滤列表将无法分配给函数的实际返回类型。要解决这个问题,只需确保T
满足超类
。使用类型参数T extends SuperClass
可以轻松实现这一点
您应该始终约束您的类型参数(请参见),除非它们的类型实际上并不重要,即使不会出错。使用不满足超类
的类调用filterList()
是没有意义的,因此如果您尝试,应该告诉编译器停止。在这种情况下,它甚至在你不尝试的情况下就阻止了你,因为它认为它本质上是不安全的
但是,您会注意到,进行更改会导致另一个错误:
“超类”可分配给类型为“T”的约束,但“T”可以用约束“超类”的不同子类型实例化
这里的问题与前面一样:函数返回typeSuperClass[]
,但它的类型为returningT[]
。如果T
是SuperClass
,那么快乐的日子就来了。但是如果它不是,并且t
是超类
的一个子类,并且有额外的成员,那么SuperClass[]
将不能分配给t[]
考虑到您正在从数组中筛选出不是类型t
的所有内容,过滤后的列表实际上是类型SuperClass[]
,这可能与直觉相反,这表明过滤后的数组应该是类型t[]
。但这样做不太管用。className的类型guardobj instanceof
创建了语义含义,即obj
是类型T
,因此从回调的那一点开始,编译器就知道了这一点。但是,对于调用回调的人,并没有提供这种含义。您的Array.prototype.filter()
调用不知道哪些类型满足其回调,因为回调没有告诉它。它所知道的只是它所过滤的数组的类型是超类[]
,因此它所能说的最好的结果就是生成的数组也是超类[]
类型
解决方法很简单:只需在回调上提供一个类型谓词(请参阅)。由此,filter()
将知道哪些类型满足其回调,从而使其能够准确地说出结果数组的类型
const foo:SuperClass[]=list.filter(obj=>obj instanceof className)
常量栏:T[]=list.filter((obj):obj是T=>obj类名称的实例)
因此,通过这两个修复,您的filterList()
函数现在看起来如下所示:
函数过滤器列表(类名:{new():T}):T[]{
return list.filter((obj):obj是T=>obj instanceof className);
}
在一边
我怀疑你在这里想做什么;总之,我不确定这是否是个好主意。您应该知道,类继承本身在类型方面没有语义意义。给定
类A{}
,新的A
将可分配给类型超类
。这不是a
实际上与超类相关的问题,而是a
满足超类的问题。我认为在你的filterList
函数中没有任何方法可以强制执行t
是超类的子类
我必须添加一个答案,因为我发现了一些原始问题没有明确提出的新信息(关于抽象类)
function filterList<T extends SuperClass>(className: Function & {prototype: T}): T[] {
return list.filter((e): e is T => e instanceof className);
}
但是,如果类属性/方法的形状不匹配,TypeScript将捕获意外传入不相关类的错误。请注意,未选中构造函数形状
class UnrelatedDummyClass{
constructor(differentConstructorParameters:number){}
}
//Property 'commonFunc' is missing in type 'UnrelatedDummyClass' but required in type 'SuperClass'.(2345)
filterList(UnrelatedDummyClass);
请参见演示:
对我来说,这个解决方案更可取,因为在我的用例中,我控制着传递给filterList
的参数,并且很容易避免传递不可构造函数的逻辑错误
我认为不可能两全其美,让参数安全性限制无效的类/函数,同时允许
function NonConstructibleFunction(){}
filterList(NonConstructibleFunction);
class UnrelatedDummyClass{
constructor(differentConstructorParameters:number){}
}
//Property 'commonFunc' is missing in type 'UnrelatedDummyClass' but required in type 'SuperClass'.(2345)
filterList(UnrelatedDummyClass);