[英]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
。
出於這個原因,即使在最簡單的情況下,將值返回到條件返回類型也是一個問題。 我們知道value
是true
,它並不總是意味着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"'
}
}
所以是的,你必須用return value as ValueFromEntry<Entries[EntryId]>
。 您已經知道該怎么做,但希望您對為什么需要它有更好的理解。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.