TLDR:Playground Repro
在我的应用程序中,我定义了多个表单模块,它们大致如下:
const firstModule = {
name: 'firstModule',
mutation: () => {
return (opts: {variables: {firstModuleArg: string}}) => {}
}
}
const secondModule = {
name: 'secondModule',
mutation: () => {
return (opts: {variables: {secondModuleArg: number}}) => {}
}
}
如您所见,每个变异函数都返回一个期望具有特殊形状的variables
字段的函数。
直接使用每个模块都可以:
firstModule.mutation()({ variables: { firstModuleArg: 'test' } }); => ok
secondModule.mutation()({ variables: { secondModuleArg: 123 } }); => ok
但是,我也正在创建这些表格的中央注册表,以便可以从其他地方像这样查找它们:
const forms = {
firstModule,
secondModule
}
const getFormConfig = (root: 'firstModule' | 'secondModule') => {
const rootObj = forms[root];
return rootObj;
}
这是问题所在。当我尝试引用组合表单对象的单个成员时,Typescript似乎正在自动创建variables
字段的交集并引发以下错误:
const { mutation: firstModuleMutation } = getFormConfig('firstModule');
firstModuleMutation()({ variables: { firstModuleArg: '1234' } });
我想我在这里缺少了一些相当简单的东西,但是希望对如何获得理想的行为有一些见识(当我专门检索firstModule
时,我只希望它从以下位置验证变量字段)该模块)。如果还有其他信息,请告诉我。
谢谢!
答案 0 :(得分:2)
以这种方式定义函数时,TypeScript会失去模块名称与突变返回类型之间的关系。
您可以使用函数重载,也可以使用类型参数定义函数。由于已经提供了第一种解决方案,因此让我介绍第二种方法。它的优点是可以无限扩展。如果您决定扩展模型,那将是可行的,而对于重载,则每次模型更改时都必须对其进行更新。
我们首先需要一些常用的助手。
select r.record_id,
a.text as a_txt,
b.text as b_txt,
c.text as c_txt
from records r
left join tablea a
on r.a_id=a.id
left join tableb b
on r.b_id=b.id
left join tablec c
on r.c_id=c.id
where r.record_id=<Some Value>;
您的域模型:
type ValueOf<T> = T[keyof T];
type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;
最终解决方案:
/**
* Type aliases.
*/
type Forms = typeof forms;
type Module = ValueOf<Forms>;
/**
* The return type for `getFormConfig`.
*/
type TransformedModule<T extends Module> = Overwrite<T, { mutation: ReturnType<T['mutation']> }>;
用法:
export function getFormConfig<K extends keyof Forms>(arg: K) {
const module = forms[arg];
return ({ ...module, mutation: module.mutation() }) as TransformedModule<Forms[K]>;
}
答案 1 :(得分:1)
您可以帮助编译器进行重载:
function getFormConfig(root: 'firstModule'):
typeof firstModule & { mutation: ReturnType<typeof firstModule.mutation> }
function getFormConfig(root: 'secondModule'):
typeof secondModule & { mutation: ReturnType<typeof secondModule.mutation> }
function getFormConfig(root: 'firstModule' | 'secondModule') {
const rootObj = forms[root];
const mutation = rootObj.mutation();
return {...rootObj, mutation}
}