打字稿神秘交集 [英] Typescript Mysterious Intersection

查看:70
本文介绍了打字稿神秘交集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TLDR:在我的应用程序中,我定义了多个表单模块,它们大致类似于:

In my application, I'm defining multiple form modules which look roughly like:

const firstModule = {
    name: 'firstModule',
    mutation: () => {
        return (opts: {variables: {firstModuleArg: string}}) => {} 
    }
}

const secondModule = {
    name: 'secondModule',
    mutation: () => {
        return (opts: {variables: {secondModuleArg: number}}) => {} 
    }
}

如您所见,每个变异函数都返回一个需要特殊形状的variables字段的函数.

As you can see, each mutation function returns a function that expects a particularly shaped variables field.

直接使用每个模块都可以:

Usage of each module directly works just fine:

firstModule.mutation()({ variables: { firstModuleArg: 'test' } }); => ok

secondModule.mutation()({ variables: { secondModuleArg: 123 } }); => ok

但是,我也正在创建这些表格的中央注册表,以便可以从其他地方像这样查找它们:

However, I'm also creating a central registry of these forms so that I can look them up from elsewhere like so:

const forms = {
    firstModule,
    secondModule
}


const getFormConfig = (root: 'firstModule' | 'secondModule') => {
    const rootObj = forms[root];

    return rootObj;
}

这是问题所在.当我尝试引用组合表单对象的单个成员时,Typescript似乎自动创建了variables字段的交集并引发以下错误:

This is where the issue is.. When I then try to refer to a single member of the combined form object, it seems like Typescript is automatically creating an intersection of the variables fields and throwing the following error:

const { mutation: firstModuleMutation } = getFormConfig('firstModule');

firstModuleMutation()({ variables: { firstModuleArg: '1234' } });

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

I imagine I'm missing something fairly simple here, but was hoping to get some insight into how to get the ideal behavior (when I specifically retrieve the firstModule, I only want it to validate the variables field from that module). Please let me know if there's any other information I can provide.

谢谢!

推荐答案

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

When a function is defined this way, TypeScript loses the relation between your module name and your mutation return type.

您可以使用函数重载,也可以使用类型参数定义函数.由于已经提供了第一种解决方案,因此让我介绍第二种方法.它的优点是可以无限扩展.如果您决定扩展模型,那将是可行的,而对于重载,则每次模型更改时都必须对其进行更新.

You can either use function overloads or define your function using type parameters. Since the first solution was already provided, let me present the second approach. Its advantage is that it scales indefinitely. If you decide to extend your model, it will just work, whereas with overloads you would have to update them every time your model changes.

我们首先需要一些常用的帮手.

We will need a few commonly used helpers first.

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

这篇关于打字稿神秘交集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆