我有一个通用方法,该方法来自RXJS库(此处简化):
function Subject<T>(t: T):T {
return t;
}
我还有一个接口(interface)声明我的应用程序值。 (可以添加键)
interface IState {
key1: number;
key2: string;
}
最后,我有一个
Store
,它通过通用函数包装器应用于带有实际值的IState
接口(interface)(通用函数是RXJS
主题的别名)let Store : IState= Subject<IState>({
key1: void 0,
key2: void 0,
})
好的,让我们添加两种从商店获取和设置的方法:
设置存储:
function set<T>(name: keyof IState, statePart: T) {
Store={
...Store,
[name]: statePart
};
用法:
set<string>("key1", "3");
此功能工作正常,只允许我使用属于
IState
的有效密钥。这里没有错误。但是看
Select
方法:(调用应类似于:)
let myVal:number = select<number>("key1");
这是方法:
function select<T>(k: keyof IState): T {
return <T>Store[k]; // <-- error here
}
问题:
这是为什么 ?如果我删除
keyof
:function select<T>(k): T {
return <T>Store[k];
}
然后它确实可以编译,但是没有任何意义,
Store
是Istate
的类型,Istate
包含Istate
的键为什么在没有
keyof
的情况下可以工作,如何修复我的代码,以便select
方法将强制仅选择Istate
的键?ONLINE DEMO
最佳答案
问题在于k
可以是IState
的任何键,因此无法确保Store[k]
与T
兼容。我将把通用参数更改为键,并根据字段类型输入结果:
function select<K extends keyof IState>(k: K): IState[K] {
return Store[k];
}
let myVal = select("key1"); // myval is number
还可以改进
set
使其不具有显式的泛型参数,并确保传递给set
的值与字段类型兼容:function set<K extends keyof IState>(name: K, statePart: IState[K]) {
Store={
...Store,
[name]: statePart
}
}
set("key1", 3);
编辑
如注释中所述,如果将鼠标悬停在调用上(至少在vscode中),则可以看到实际的推断类型:
如果您要保留显式类型参数,尽管我不建议您这样做,因为您很容易使字段类型和调用类型不匹配,但是可以通过
any
使用类型断言:function select3<T>(k: keyof IState): T {
return Store[k] as any;
}