Typescript静态类泛型函数

时间:2017-12-20 18:02:48

标签: typescript generics

尝试更彻底地理解typescript泛型+静态类函数,并且我遇到了一些类型问题:

class Base {
  value : string = 'default'

  static identity<M extends typeof Base>() : M  {
    return this // Type Error: Type 'typeof Base' is not assignable to type 'M'
  }
}

class Extended extends Base {
  otherValue = 'extended'

  constructor() {
    super()
  }
}

let baseClass = Base.identity()
let extendedClass = Extended.identity()
expect(new baseClass().value).to.eq('default')

// Type Error: Property 'otherValue' does not exist on type 'Base'
// How do I make Extended.identity() be `typeof Extended` without
// an explicit cast?
expect(new extendedClass().otherValue).to.eq('extended')

如果我忽略类型错误并运行输出代码,一切都按预期运行并满足期望。我认为这可能是由于与静态函数存在一些理解上的差距,但是理解这个问题的任何帮助都将非常受欢迎。

3 个答案:

答案 0 :(得分:2)

看起来你真正想要的是polymorphic this on static methods,TypeScript不直接支持。 所做的似乎支持(并且它是mentioned in that Github issue),在方法上使用this parameters

class Base {
  value : string = 'default'

  // specify that this is M
  static identity<M extends typeof Base>(this: M) : M  {
    return this; 
  }
}

class Extended extends Base {
  otherValue = 'extended'

  constructor() {
    super()
  }
}

静态identity()方法仅在您调用它的对象可分配给Base构造函数类型的子类型时才有效。当你调用它时,编译器显然会使用类型推断来确定M的正确值。您可以验证事情的行为符合预期:

let baseClass = Base.identity() // Base.identity<typeof Base>
let extendedClass = Extended.identity() // Base.identity<typeof Extended>

它没有很好的记录,坦率地让我感到惊讶,你实际上可以让它工作。如果您真的希望静态方法中的多态this能够在没有上述巫术的情况下工作,那么您可能需要转到Microsoft/TypeScript#5863并提出问题或描述您的用例,如果您认为它特别引人注目的话。 / p>

希望有所帮助;祝你好运!

答案 1 :(得分:1)

你的问题是M比Base更具体,你试图将Base分配给M.想象一下:

  • 您有班级Base
  • 另一个课程延伸Base - &gt; Base+A(A表示较不通用的类具有的其他属性)
  • 您在Base.identity类上调用Base+A方法:

    identity<Base+A extends typeof Base>(): Base+A {
      return this; // This is a Base class method, so it's typeof Base
    }
    

    编译器在返回值的类型中找不到A部分,因此它会抛出错误。

答案 2 :(得分:0)

扩展@jcalz支持实例类型的答案(适用于ts@3.5):

class A {
  static a<T extends typeof A>(this: T) {
    return new this();
  }

  static b<T extends typeof A, I = InstanceType<T>>(this: T): I {
    return (new this()) as I;
  }
}

class B extends A {}

let a = A.a();  // type: A
let b = B.a();  // type: A

let a2 = A.b(); // type: A
let b2 = B.b(); // type: B

相关问题