打字稿神秘交集

时间:2019-02-15 17:24:32

标签: javascript typescript typescript3.0

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' } });

Typescript Error

我想我在这里缺少了一些相当简单的东西,但是希望对如何获得理想的行为有一些见识(当我专门检索firstModule时,我只希望它从以下位置验证变量字段)该模块)。如果还有其他信息,请告诉我。

谢谢!

2 个答案:

答案 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}
}