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())
