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`
}
答案#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 并返回一个正确的实例,所以当在 MyArray 上调用 map 时,我们仍然得到一个 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
答案#2
如果我理解正确的话,你所寻找的是 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())
