1

TLDR:游乐场再现

在我的应用程序中,我定义了多个表单模块,大致如下:

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,我只希望它验证该模块中的 variables 字段)。如果我可以提供任何其他信息,请告诉我。

谢谢!

4

2 回答 2

2

当以这种方式定义函数时,TypeScript 会丢失模块名称和突变返回类型之间的关系。

您可以使用函数重载或使用类型参数定义函数。由于已经提供了第一种解决方案,让我介绍第二种方法。它的优点是可以无限扩展。如果您决定扩展您的模型,它会正常工作,而对于重载,您必须在每次模型更改时更新它们。

我们首先需要一些常用的助手。

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]>;
}

用法:

getFormConfig('firstModule').mutation({ variables: { firstModuleArg: 'foo' } })
getFormConfig('secondModule').mutation({ variables: { secondModuleArg: 42 } });
于 2019-02-15T18:05:31.950 回答
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}
}
于 2019-02-15T17:45:38.780 回答