簡體   English   中英

TypeScript - 包含其他泛型類型的 class 字典泛型 - 泛型類型在方法內部丟失的問題

[英]TypeScript - class dictionary generic that holds other generic types - problem with generic types being lost inside method

我有一個半復雜的例子,其中有一個 class Store ,它有一個其他“值類”( Alpha<AlphaVal>Beta<BetaVal> )的字典作為它的通用參數,它有一個從該字典返回值的get方法( “價值類別”持有的價值)。

我可以為get的返回值創建類型,它可以正常工作。 但是,我對get內部發生的事情有疑問。

由於某種原因,這些值類的泛型丟失了。 在使用自定義類型謂詞來縮小類型時尤其丟失,但我認為問題不只是自定義類型保護,而是get方法內部的聯合類型entry已經丟失了 generics。

你怎么看,它是一個錯誤還是有正當理由它以它的工作方式工作? 如果是后者,有沒有辦法修改自定義類型守衛isAlpha以保留泛型類型?

游樂場鏈接

// Base types for value classes
type AlphaVal = { [index: string]: any}; 
type BetaVal = number | string | boolean;

// Value class - holds a value
class Alpha<T extends AlphaVal> {
  private value: T;
  public isAlpha() {  }
  public get(): T { return this.value; }
}

class Beta<T extends BetaVal> {
  private value: T;
  public isBeta() { };
  public get(): T { return this.value }
}

// type AsAlpha<T> = Extract<T, Alpha<AlphaVal>>;
type AsAlpha<T> = T extends Alpha<infer R>
  ? Extract<T, Alpha<R>> : Extract<T, Alpha<AlphaVal>>;

// The type guard
const isAlpha = <V extends Alpha<AlphaVal> | Beta<BetaVal>>(
  value: V
): value is AsAlpha<V> => {
    return (value instanceof Alpha);
}

// Converts Alpha<R> to R, Beta<R> to R
type ValueFromEntry<T extends Alpha<AlphaVal> | Beta<BetaVal>> =
  T extends Alpha<infer R>
  ? R : T extends Beta<infer R>
  ? R : unknown;

class Store<Entries extends { [index: string]: Alpha<AlphaVal> | Beta<BetaVal> }> {
    private entries = { } as Entries;

    // Gets entry value
    public get<EntryId extends keyof Entries>(
        entryId: EntryId
    ): ValueFromEntry<Entries[EntryId]> { // return type works properly
        //
        let entry = this.entries[entryId];

        if (isAlpha(entry)) {
            entry.isAlpha(); // just ensuring that the type guard works
            let value = entry.get();
            // The problem here is that the type is Alpha<AlphaValue>
            // and the generic is lost, so it's not assignable to the return value
            return value;

            // of course something like below would work, but I'm trying to
            // find out if there's a better way.
            // return value as ValueFromAlphaBeta<Entries[EntryId];
        }

        // However, it may not be just an issue with the type guard, but with
        // how the entry type is typed inside the function.
        // If you hover on entry.get() below, it's set to return AlphaVal | BetaVal,
        // it appears the generic is lost even without type narrowing.
        return entry.get();

        // The interesting thing is that it works properly on the return type.
    }
}

type SampleStore = {
    a: Alpha<{ test: true }>
    b: Beta<5>
}

const sampleStore = new Store<SampleStore>();

// types on return value work properly, generic is preserved
let value = sampleStore.get('a'); 

您的代碼很復雜,但問題的核心可以歸結為非常簡單的事情。 當您檢查if (isAlpha(entry))時,這會縮小變量entry的類型。 它不會縮小泛型類型參數EntryId的類型。

您無法知道條目類型是AlphaVal並且只有AlphaVal ,因為類型EntryId可能是更廣泛的類型,例如'alpha1' | 'alpha2' | 'beta1' 'alpha1' | 'alpha2' | 'beta1' 'alpha1' | 'alpha2' | 'beta1'甚至只是keyof Entries

出於這個原因,即使在最簡單的情況下,將值返回到條件返回類型也是一個問題。 我們知道valuetrue ,它並不總是意味着T extends true

const test = <T extends boolean>(value: T): T extends true ? "A" : "B" => {
    if ( value ) {
        return "A"; // error: Type '"A"' is not assignable to type 'T extends true ? "A" : "B"'
    } else {
        return "B"; // error: Type '"B"' is not assignable to type 'T extends true ? "A" : "B"'
    }
}

Typescript 游樂場鏈接

所以是的,你必須用return value as ValueFromEntry<Entries[EntryId]> 您已經知道該怎么做,但希望您對為什么需要它有更好的理解。

暫無
暫無

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

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