[英]Infer specific type from an object associated to a specfic object's key
我有一个形状为{[key]: <T>()=>T}
的注册表 object, T
绝对可以是任何东西,因为我希望将来可以轻松地向其中添加更多条目。 然后我有一个 function 接受注册表项作为字符串,然后这个 function 应该返回关联的泛型类型的值。 但我无法让它工作,因为它返回注册表中所有函数返回的泛型类型的联合。
描述有点笨拙,所以我希望这个简单的工作示例能够足够详细地描述我打算做什么。
type valFn<T> = () => T
declare const stringFn: valFn<string>;
declare const numberFn: valFn<number>;
const registry = {
alpha: stringFn,
beta: numberFn,
// ... i want to easily add some records in the future without modifying +registryEater+ every time i do so, all the records will be of type +key: ValFn<Something>+
};
// CHANGES CAN BE MADE ONLY DOWN FROM HERE
type ReturnType<T> = T extends valFn<infer R> ? R : never
const registryEater = <T extends keyof typeof registry>(registryKey: T): ReturnType<typeof registry[T]> => {
const desiredFn = registry[registryKey];
const desiredValue = desiredFn();
// TS2322 because +desiredValue+ is string | number, but i need it do be a specific type depending on real value of +registryKey+
return desiredValue;
};
// CHANGES CAN BE MADE ONLY UP FROM HERE
const thisIsString = registryEater('alpha');
const thisIsNumber = registryEater('beta');
我可以通过更改return desiredValue;
以return desiredValue as ReturnType<typeof registry[T]>;
,但是没有更清洁的方法吗?
编译器实际上无法对依赖泛型类型参数的条件类型进行太多类型分析,例如ReturnType<typeof registry[T]>
。 如果您想以编译器“理解”的方式编写您的 function,您应该重构为使用分布式 object 类型,如microsoft/TypeScript#47109中所述。 这个想法是从一个非常基本的映射类型开始,比如
interface RegistryReturn {
alpha: string;
beta: number;
}
然后将您的其他操作表示为映射类型:
type Registry = { [K in keyof RegistryReturn]: () => RegistryReturn[K] }
const registry: Registry = {
alpha: stringFn,
beta: numberFn
}
您的通用 function 然后可以索引到该映射类型,并且一切正常:
const registryEater = <K extends keyof RegistryReturn>(
registryKey: K
): RegistryReturn[K] => {
const desiredFn = registry[registryKey];
const desiredValue = desiredFn();
return desiredValue; // okay
};
desiredFn
变量被视为Registry[K]
类型,然后desiredValue()
被视为RegistryReturn[K]
类型,特别是因为Registry
被定义为映射类型,如 microsoft/TypeScript#47109 中所述。
这样就可以根据需要进行编译,但不幸的是,它根据Registry
重新定义了registry
,这是不允许您这样做的。 相反,您的要求是registry
已提供给您并且无法修改。 幸运的是,我们可以根据它定义RegistryReturn
映射类型:
type RegistryReturn = { [K in keyof typeof registry]:
ReturnType<typeof registry[K]>
}
然后只需将registry
分配给Registry
类型的新变量,这样映射类型查找仍然会发生:
const registryEater = <K extends keyof RegistryReturn>(
registryKey: K
): RegistryReturn[K] => {
const _registry: Registry = registry; // new variable
const desiredFn = _registry[registryKey];
const desiredValue = desiredFn();
return desiredValue; // still okay
};
那仍然编译。 剩下要做的唯一一件事就是确保调用者获得他们想要的行为:
const thisIsString: string = registryEater('alpha'); // okay
const thisIsNumber: number = registryEater('beta'); // okay
看起来不错!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.