我見過的大多數 Maybe/Option 型別的 Typescript 實作要么使用帶有標簽的介面來區分 Some/None 變體。我見過一些沒有并且實際上有 Some 和 None 的單獨類,它們都實作了一個通用的 Option 介面。作為一個實驗,我想嘗試一個使用繼承的實作。所以這是我想出的簡化版本:
abstract class Maybe<T> {
constructor(private value: T) {}
isSome(): this is Some<T> {
return this instanceof Some;
}
isNone(): this is None {
return !this.isSome();
}
and<U>(other: Maybe<U>): Maybe<U> {
return this.isNone() ? new None() : other;
}
unwrap(): T {
if (this.isNone()) throw new Error('called unwrap() on None');
return this.value;
}
unwrapOr(defaultValue: T): T {
return this.isNone() ? defaultValue : this.value;
}
}
class Some<T> extends Maybe<T> {
constructor(value: T) { super(value); }
}
class None extends Maybe<any> {
constructor() { super(null); }
}
但是我extends Maybe<...>在None課堂上傳遞的型別遇到了一些問題。我已經嘗試使用never,null并且any他們都導致了(不同的)編譯器的錯誤,逼我做的里面的一些奇怪的型別轉換Maybe's方法。
您可以在此處找到上述代碼的游樂場。
這是我的想法。泛型 Type TonMaybe表示包含值的型別,可以是任何型別。但是當Maybe是 a 時None,它真的應該是 a never,因為從概念上講,它沒有包含的值。與此保持一致,我的第一個想法是進行None擴展,Maybe<never>但隨后呼叫super(null)觸發編譯器錯誤,因為null不可分配給 type never。
我想出的最有用的版本是Noneextends 的版本,Maybe<any>但這需要(<Some<T>this).value對Maybes 方法的內部進行大量轉換(上面的代碼中沒有顯示),我不喜歡這樣。理想情況下,我想找到一個不需要在any所有地方使用和強制轉換的實作。任何有助于實作這一目標的幫助將不勝感激。
uj5u.com熱心網友回復:
使其作業所需的代碼的最小更改可能是這樣的:
class None extends Maybe<never> {
constructor() { super(null!); }
}
也就是說,我們說None是Maybe<never>,這里的never型別是底部型別沒有值。從型別系統的角度來看,這是合理的:“正確”型別類似于forall T. Maybe<T>,但由于 TypeScript 沒有像microsoft/TypeScript#17574這樣的“通用值”,因此無法直接表達這種型別。既然它看起來Maybe<T>應該是協變的T,那么你可以改寫forall T. Maybe<T>為Maybe<forall T. T>,這相當于Maybe<never>TypeScript 中所有型別的交集是never。
但是為了使用您的代碼構造a Maybe<never>,您需要為never超級建構式提供一個型別的值。這當然不可能發生。您正在通過null,這不是never. 我們可以使用非空斷言運算子(后綴!)向下轉換null為never(null!is NonNullable<null>which is的型別never)。這在技術上是一個謊言,但它是一個相當無害的謊言,特別是如果你以后從不嘗試觀察該值。
如果你想在任何地方都輸入安全,那么我認為你需要重構你的基類,這樣它就不會假裝做它不能做的事情。AMaybe<T>可能沒有,value因此value基類中可能不需要。它可能是可選的:
abstract class Maybe<T> {
constructor(public value?: T) { } // <-- optional
unwrap(): T {
if (this.isSome()) return this.value; // reverse check
throw new Error('called unwrap() on None');
}
unwrapOr(defaultValue: T): T {
return this.isSome() ? this.value : defaultValue; // reverse check
}
}
class Some<T> extends Maybe<T> {
declare value: T // <-- narrow from optional to required
constructor(value: T) { super(value); }
}
class None extends Maybe<never> {
constructor() { super(); } // <-- don't need an argument now
}
然后你可以要求declare子類中的屬性(我必須這樣做,public因為private不讓子類查看事物,甚至protected對你擁有的那些型別保護函式感到懷疑)。請注意,我打開你的支票從isNone()到isSome()...的Maybe<T>類不知道是一個工會的Some<T> | None,所以你不能使用虛假的結果isNone()得出結論,this.value是存在的。相反,您應該使用isSome().
最后,您可以將所有Some/None特定功能移出Maybe,然后不再擔心試圖強制基類表現得像兩個子類。基于繼承的多型性傾向于使用子類來覆寫方法,而不是使用超類方法來檢查instanceof. 這類似于Maybe只是一個interface,除了不需要檢查當前類的任何真正的多型方法:
abstract class Maybe<T> {
abstract isSome(): this is Some<T>;
abstract isNone(): this is None;
abstract and<U>(other: Maybe<U>): Maybe<U>;
abstract unwrapOr(defaultValue: T): T;
}
class Some<T> extends Maybe<T> {
private value: T
constructor(value: T) { super(); this.value = value; }
isSome(): this is Some<T> { return true; }
isNone(): this is None { return false; }
and<U>(other: Maybe<U>) {
return other;
}
unwrap() { return this.value }
unwrapOr(defaultValue: T) { return this.value }
}
class None extends Maybe<never> {
isSome<T>(): this is Some<T> { return false }
isNone(): this is None { return true }
and<U>(other: Maybe<U>) {
return this;
}
unwrapOr<T>(defaultValue: T) { return defaultValue }
}
在這里,Maybe除了抽象方法之外什么都沒有。如果你能拿出的東西并不需要檢查,如果this.isSome()還是this.isNone()那么它可以在基類中去。這里唯一值得注意的是,一些子類化Maybe<never>涉及將非泛型方法 (isSome和unwrapOr) 轉換為泛型方法。
Playground 代碼鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/407840.html
標籤:
上一篇:在java中繼承多個抽象類
