我有这种情况:
abstract class AbstractClass<T> {
abstract getData(): T;
getBase(): Partial<T> {
return {};
}
}
interface Contract {
prop1: string;
prop2: string;
}
class Impl extends AbstractClass<Contract> {
get prop1() {
// some complex logic here
return '';
}
getBase() {
return {
prop2: 'foo'
}
}
}
我如何表达约束 AbstractClass
的正确实施必须涵盖Contract
界面的所有属性?简单的解决方案是Impl implements Contract
,但是我将不得不为没有复杂逻辑getter的所有属性复制声明。所以能够使用推断类型的getBase()
实现会很高兴。
如果Impl
本身没有提供任何值,或者作为推断返回类型getBase()
的属性,则目标是产生编译时错误。
原则上可以使用Typescript吗?
答案 0 :(得分:2)
如果使getBase
为abstract并指定返回必须是当前类和接口的属性的差异,那么如果属性不在{的结果中,则会出现编译时错误{1}}或类:
getBase
您会注意到我必须将类本身作为类型参数传递给基类,使用abstract class AbstractClass<T, TThis> {
abstract getBase(): { [P in Exclude<keyof T, keyof TThis>]: T[P] };
}
interface Contract {
prop1: string;
prop2: string;
}
class Impl extends AbstractClass<Contract, Impl> {
get prop1() {
// some complex logic here
return '';
}
getBase() {
return {
prop2: ""
}
}
}
class ImplWrong extends AbstractClass<Contract, ImplWrong> {
get prop1() {
// some complex logic here
return '';
}
getBase() { // error Property 'getBase' in type 'ImplWrong' is not assignable to the same property in base type
return {
prop3: ""
}
}
}
类型不是解决方案,因为this
的键从未完全知晓。
同样this
必须至少返回getBase
和Impl
之间的差异,但它可以返回更多属性(typescript允许实现返回实现方法的超类型)。因此,如果您在Contract
类中返回prop1
,则不会出错。
答案 1 :(得分:2)
另一个解决方案是为Impl
类添加约束,并保留getBase
返回类型:
abstract class AbstractClass<T> {
abstract getData(): T;
getBase(): Partial<T> {
return {};
}
}
interface Contract {
prop1: string;
prop2: string;
}
type ImplConstraint<T, I extends AbstractClass<T>> =
{ [n in Exclude<keyof T, keyof ReturnType<I['getBase']>>]: T[n] };
class Impl extends AbstractClass<Contract> implements ImplConstraint<Contract, Impl> {
get prop1() {
// some complex logic here
return '';
}
getBase() {
return {
prop2: 'foo'
}
}
getData(): Contract {
return {} as Contract;
}
}
class ImplWrong extends AbstractClass<Contract> implements ImplConstraint<Contract, ImplWrong> {
// Class 'ImplWrong' incorrectly implements interface
// 'ImplConstraint<Contract, Impl>'
// Property 'prop1' is missing in type 'ImplWrong'.
get prop11() {
// some complex logic here
return '';
}
getBase() {
return {
prop2: 'foo'
}
}
getData(): Contract {
return {} as Contract;
}
}