TypeScript多态返回类型

时间:2019-02-10 19:30:36

标签: typescript

假设我们要实现数据类型层次结构,应该使用哪种类型?

interface Collection<T> {
  map(f: (v: T) => T): ???
}

interface Array<T> extends Collection<T> {
  map(f: (v: T) => T): ??? { return new Array<T>() }
}

interface LinkedList<T> extends Collection<T> {
  map(f: (v: T) => T): ??? { return new LinkedList<T>() }
}

多边形this无效

interface Collection<T> {
  map(f: (v: T) => T): this
}

interface Array<T> extends Collection<T> {
  map(f: (v: T) => T): this { return new Array<T> }
  // ^ error `Error: type Array<T> is not assignable to type this`
}

2 个答案:

答案 0 :(得分:1)

多态this的诚实使用是当您实际返回this时使用。考虑以下类

export interface Collection<T> {
  map(f: (v: T) => T): this
}

class Array<T> implements Collection<T> {
  map(f: (v: T) => T): this { return new Array<T>() as this; }
}

class MyArray<T> extends Array<T>{ }

new MyArray().map(o=> true) instanceof MyArray // false, but map returns MyArray

什么也没有强迫MyArray覆盖map并返回正确的实例,因此,在map上调用MyArray时,我们仍然会得到Array的实例(有点我可能会惊讶地说)。

话虽这么说,在类型系统中没有很好的方法对此建模,这是一个类,当派生该类时必须实现特定的方法而该类不是抽象的。

您可以使用多态this并使用constructor属性来防止上述情况,但是无法将派生的构造函数限制为没有参数(如下面的调用所要求) 。因此,此解决方案不是完全类型安全的,并且确实使用了一些类型断言来完成工作,但我认为它和它一样安全:

 interface Collection<T> {
  map(f: (v: T) => T): this
}

class Array<T> implements Collection<T> {
  map(f: (v: T) => T): this { return new (this.constructor as any)(); }
}

class MyArray<T> extends Array<T>{ }

console.log(new MyArray().map(o=> true) instanceof MyArray) // ture

答案 1 :(得分:1)

如果我理解正确,那么您正在寻找的是 f界多态性(“重载this”)。

interface Collection<T> {
  map(this: Collection<T>, f: (v: T) => T): this;
}

this的类型始终用作第一个参数。注意,我们可以添加更多的重载。例如,要将Collection<number>转换为Collection<string>,我们可以这样做:

interface Collection<T> {
  map(this: Collection<T>, f: (v: T) => T): this;
  map<U>(this: Collection<T>, f: (v: T) => U): Collection<U>;
}

用法:

declare const collection: Collection<number>;

collection.map(x => x.toString())