我有一个简单的IFactory<OUT>
界面和两个派生类。
export interface IFactory<OUT = any> {
create(): OUT;
}
// number implementation
export class NumberFactory implements IFactory<number> { ... }
// string implementation
export class StringFactory implements IFactory<string> { ... }
还有一种calculate()
使用给定模型构建对象的方法。
对于以下模型;
const model: Record<string, IFactory> = {
num: new NumberFactory(),
str: new StringFactory()
}
该calculate()
方法返回一个类似于下面的对象;
const result = calculate(model);
/// result
/// {
/// num: 1,
/// str: "str"
/// }
问题是,我怎么知道结果的类型?我尝试内置打字机实用程序,但无法实现。
谢谢您的帮助。
如果您将注释 model
为Record<string, IFactory>
,那么您已经迷路了。这导致编译器忘记了其中的任何特定键-值关系model
,而是将其一直扩展到具有任何可能的键且其属性为任何可能的IFactory
类型的事物。如果您想从中添加/修改/删除属性,那就太好了model
。否则,您可能应该让编译器为您推断类型model
:
const model = {
num: new NumberFactory(),
str: new StringFactory()
}
/* const model: {
num: NumberFactory;
str: StringFactory;
} */
现在,您将有机会calculate()
跟踪输入类型,以使输出达到您的期望。这是一种可能的实现:
function calculate<T extends object>(model: { [K in keyof T]: IFactory<T[K]> }) {
return Object.fromEntries(
Object.entries(
model as Record<string, IFactory>
).map(([k, v]) => [k, v.create()])
) as T;
}
在这里,我使用来自映射类型的推论来查看calculate()
某种对象类型的输出,T
以及将输入作为映射版本的输入,其中每个属性T[K]
都包裹在一起IFactory
以产生IFactory<T[K]>
。
在函数体内,编译器无法真正跟踪或验证实现是否正确执行了此操作,因此我使用一些类型断言来告诉它不要担心。这给我增加了类型安全的负担,因此我必须小心。
让我们测试一下:
const result = calculate(model);
// const result: {
// num: number;
// str: string;
// }
console.log(result);
/// result
/// {
/// num: 1,
/// str: "str"
/// }
看起来不错。calculate()
接受model
并推断T
为{num: number; str: string;}
,实现也可以在运行时运行。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句