簡體   English   中英

使用 class inheritance 在 TypeScript 中實現 Maybe/Option 類型

[英]Implementing Maybe/Option type in TypeScript using class inheritance

我見過的大多數 Maybe/Option 類型的 Typescript 實現要么使用帶有標簽的接口來區分 Some/None 變體。 我見過一些沒有並且實際上有 Some 和 None 的單獨類,它們都實現了一個通用的 Option 接口。 作為實驗,我想嘗試使用 inheritance 的實現。 所以這是我想出的簡化版本:

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); }
}

但是我遇到了一些問題,即在None class 上將類型傳遞給extends Maybe<...> 我嘗試過使用nevernullany ,它們都會導致(不同的)編譯器錯誤,迫使我在Maybe's方法中進行一些奇怪的轉換。

您可以在此處找到上述代碼的游樂場。

這是我的想法。 Maybe上的泛型 Type T表示包含值的類型,可以是任何類型。 但是當MaybeNone時,它真的應該是never ,因為從概念上講,它沒有包含的值。 與此保持一致,我的第一個想法是讓None擴展Maybe<never>但隨后對super(null)的調用會觸發編譯器錯誤,因為null不可分配給類型never

我想出的最有用的版本是None擴展Maybe<any>的版本,但這需要在Maybe的方法中進行一堆(<Some<T>this).value的轉換(上面的代碼中沒有顯示)和我不喜歡那樣。 理想情況下,我想找到一個不需要使用any並在所有地方強制轉換的實現。 任何有助於實現這一目標的幫助將不勝感激。

使其工作所需的代碼的最小更改可能是這樣的:

class None extends Maybe<never> {
    constructor() { super(null!); }
}

也就是說,我們說NoneMaybe<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

但是為了用你的代碼構造一個Maybe<never> ,你需要為超級構造函數提供一個never類型的值。 這當然不可能發生。 您正在傳遞null ,這不是never 我們可以使用非空斷言運算符(后綴!null向下轉換為nevernull!的類型是NonNullable<null> ,它是never )。 這在技術上是一個謊言,但它是一個相當無害的謊言,特別是如果你以后從不嘗試觀察該值。


如果你想在任何地方都輸入安全,那么我認為你需要重構你的基礎 class ,這樣它就不會假裝做它不能做的事情。 Maybe<T>可能沒有value ,因此在基本 class 中可能不需要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> class 不知道是Some<T> | None聯合 Some<T> | None ,因此您不能使用isNone()的錯誤結果來斷定this.value存在。 相反,您應該使用isSome()的真實結果。


最后,您可以將所有Some / None特定功能從Maybe中移出,然后不再擔心試圖強制基礎 class 表現得像兩個子類。 基於繼承的多態性傾向於使用子類來覆蓋方法,而不是使用檢查instanceof的超類方法。 這類似於Maybe只是一個interface ,除了不需要檢查當前 class 的任何真正多態的方法:

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()的東西,那么它可以在基礎 class 中找到 go。 這里唯一值得注意的是, Maybe<never>的一些子類化涉及將非泛型方法( isSomeunwrapOr )轉換為泛型方法。


Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM