假设我们要实现数据类型层次结构,应该使用哪种类型?
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`
}
答案 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())