使用推断方法返回类型的Typescript泛型约束

时间:2018-05-09 16:43:13

标签: typescript

我有这种情况:

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吗?

2 个答案:

答案 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必须至少返回getBaseImpl之间的差异,但它可以返回更多属性(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;    
  }  

}