【发布时间】:2021-07-13 11:21:02
【问题描述】:
我有一个半复杂的例子,其中有一个类 Store 有一个其他“值类”(Alpha<AlphaVal>,Beta<BetaVal>)的字典作为它的通用参数,它有一个 get 方法返回该字典中的值(由“值类”保存的值)。
我可以为get 的返回值创建类型并且它可以正常工作。但是,我对 get 内部发生的事情有疑问。
由于某种原因,这些值类的泛型丢失了。在使用自定义类型谓词来缩小类型时尤其会丢失,但我认为问题不仅在于自定义类型保护,还在于 get 方法内的 entry 联合类型已经失去了泛型。
您怎么看,这是一个错误,还是有正当理由以它的工作方式工作?如果是后者,有没有办法修改自定义类型保护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');
【问题讨论】:
标签: typescript typescript-typings typescript-generics