Why is this TypeScript class implementing the interface not assignable to a generic constraint extending the interface?

时间秒杀一切 提交于 2021-01-28 07:09:14

问题


I'm trying to develop some interfaces for linear algebra functions, because the world needs another linear algebra library. The (possibly bad) idea is to be able to specify Vector or Matrix for some higher level functions and have it just work, as long as the dimensions are correct.

I ran into some problems doing this with the Vector interface, but I discovered using this as a type and it resolved my issues (maybe I'm doing a bad thing here). I tried doing this in the Matrix interface, but because the arguments to the functions aren't of type Matrix, the this trick doesn't work.

I'm getting this same error for a lot of functions like this, but this is one example:

interface Vector {
  set (...args: number[]): this;
  setScalar (scalar: number): this;
  clamp (min: this, max: this): this;
  ...
}

class Vector2 implements Vector { ... }

interface Matrix {
  elements: number[];
  getRow<T extends Vector> (n: number): T;
  ...
}

class Matrix2 implements Matrix {
  private _elements: number[];
  public getRow (i: number): Vector2 {
    const te = this._elements;
    switch (i) {
      case 0:
        return new Vector2(te[0], te[2]);
      case 1:
        return new Vector2(te[1], te[3]);
      default:
        throw new Error('No row defined at ' + i + '.');
    }
  }
}

Constructing the interface this way, I get the following error message:

Property 'getRow' in type 'Matrix2' is not assignable to the same property in base type 'Matrix'.
  Type '(i: number) => Vector2' is not assignable to type '<T extends Vector>(n: number) => T'.
    Type 'Vector2' is not assignable to type 'T'.ts(2416)

Vector2 implements the Vector interface, and the generic type constraint on getRow() requires the return type to be a type that implements the Vector interface.

Why I can't do things this way? I don't understand what this message is trying to tell me. I know I could probably work around this, but I also want to understand more about what's going on and maybe learn (!) something.

I'm using TypeScript 3.6.3.


回答1:


I think the problem here is that the generic T can be specified at the call site, but m.getRow always returns a Vector2, no matter what the call site specifies for T.

For example, the following would be valid TypeScript, but doesn't work in your case. To prevent this situation from occurring, TypeScript will not compile and throw an error instead.

class Vector3 extends Vector { }

const m = new Matrix()
const row = m.getRow<Vector3>(0) // You specify a Vector3, but get a Vector2.

The easiest solution here would be to remove the generic and just declare getRow to always return a Vector:

interface Matrix {
  elements: number[];
  getRow(n: number): Vector;
}


来源:https://stackoverflow.com/questions/57927538/why-is-this-typescript-class-implementing-the-interface-not-assignable-to-a-gene

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!