typescript - TypeScript - 包含其他泛型类型的类字典泛型 - 泛型类型在方法中丢失的问题
问题描述
我有一个半复杂的示例,其中有一个类Store
具有其他“值类”(Alpha<AlphaVal>
,Beta<BetaVal>
)的字典作为其通用参数,并且它具有get
从该字典返回值(由“值类”保存的值)的方法。
我可以为返回值创建类型get
并且它可以正常工作。但是,我对内部发生的事情有疑问get
。
由于某种原因,这些值类的泛型丢失了。在使用自定义类型谓词来缩小类型时尤其会丢失,但我认为问题不仅在于自定义类型保护,还在于entry
内部get
方法的联合类型已经失去了泛型。
你怎么看,它是一个错误还是有正当理由它以它的工作方式工作?如果是后者,有没有办法修改自定义类型保护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');
解决方案
Your code is complex but the core of the issue can be boiled down to something really simple. When you check if (isAlpha(entry))
, this narrows the type of the variable entry
. It does not narrow the type of the generic type parameter EntryId
.
You cannot know that the entry type is AlphaVal
and only AlphaVal
because the type EntryId
could be a broader type like 'alpha1' | 'alpha2' | 'beta1'
or even just keyof Entries
.
Returning a value to a conditional return type is a problem for this reason even in the simplest of cases. We we know that value
is true
, it doesn't always mean that 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"'
}
}
So yes you have to just assert correctness with return value as ValueFromEntry<Entries[EntryId]>
. Which you already know how to do, but hopefully you have a better understanding of why it's needed.
推荐阅读
- reactjs - 当属性名称可以变化时解构
- c# - 如何将excel数据从openxml worksheetpart.worksheet c#中获取到GetBytes的数组中
- visual-studio-code - VS Code TypeScript 自动导入建议优先级
- smartsheet-api - 在 Swift 中使用 Smartsheet API
- java - 如何创建组合框按钮
- javascript - 在 React Native 中有条件地渲染图标
- android - API 19 上的惰性导致的应用程序崩溃
- sql - Postgresql 字符串到 jsonb_each_text()
- javascript - 将 JSON 数据从 Javascript 传递到 OPENJSON MSSQL
- batch-file - 如果我有一个批处理文件使用下面的代码调用 txt 文件并且我的预期结果是 MyFile_20200804.zip,我在这里做错了什么