我想在TypeScript中设计一个函数,该函数访问复杂结构的字段名并返回该字段的类型。例如,有一个Settings
类型有几个字段:
export interface Settings {
apiConfig?: ApiConfig;
presentation?: PresentationSettings;
}
我想要一个函数getSettings
,可以用作:
let cfg: ApiConfig = getSettings('apiConfig');
let ui: PresentationSettings = getSettings('presentation');
我的第一个方法是使用密钥:
function getSettings(name: keyof Settings): Settings[keyof Settings] {
if (name == 'apiConfig') {
return store.get(name) as ApiConfig;
}
if (name == 'presentation') {
return store.get(name) as PresentationSettings;
}
}
但这意味着返回类型基本上是ApiConfig|PresentationSettings的并集。所以我必须投:
const ui = <PresentationSettings>getSettings('presentation');
不理想。
下一个方法是仿制药:
export function getSettings<T extends Settings[keyof Settings]>(
name: keyof Settings
它可以用作:
const ui = getSettings<PresentationSettings>('presentation');
但在实现功能时:
export function getSettings<T extends Settings[keyof Settings]>(
name: keyof Settings
{
if (name == 'apiConfig') {
return store.get(name) as ApiConfig;
}
}
我出错了:类型"ApiConfig"不可分配给类型"T"。"ApiConfig"可分配给类型为"T"的约束,但"T"可以用约束的另一个子类型"ApiCnfig|PresentationSettings"实例化。ts(2322(
所以它必须转换为any
。也不理想。
还有其他办法吗?
特别是我想称之为不带任何演员阵容:const ui=getSettings("表示"(;并且具有CCD_ 4的实例。这可能吗?
函数肯定需要是泛型的。但是T
不应该是返回类型,而应该只是键。返回类型可以使用T
对Settings
进行索引。
function getSettings<T extends keyof Settings>(name: T): Settings[T] {
return {
apiConfig: store.get(name) as ApiConfig,
presentation: store.get(name) as PresentationSettings
}[name]
}
如果您希望实现中没有错误,您将不得不使用某种映射结构,如上面所示。
此实现的问题是,对象中的每个store.get
调用都将被急切地执行,如果这是一个昂贵的调用,则这是不理想的。为了避免这种情况,您可以只使用普通的switch
语句。但你需要一些断言来掩盖错误。
function getSettings<T extends keyof Settings>(name: T): Settings[T] {
switch (name) {
case "apiConfig": { return store.get(name) as Settings[T] }
case "presentation": { return store.get(name) as Settings[T] }
default: { return undefined as Settings[T] }
}
}
如果您希望完全类型安全,并且不急于执行所有路径,则可以使用包含函数的映射。这里唯一的缺点是额外的函数调用,而且它可能看起来过于冗长。
function getSettings<T extends keyof Settings>(name: T): Settings[T] {
const mapping: {
[K in keyof Settings]: () => Settings[K]
} = {
apiConfig: () => store.get(name) as ApiConfig,
presentation: () => store.get(name) as PresentationSettings
}
return mapping[name]()
}
游乐场