Skip to content Skip to sidebar Skip to footer

Typescript Mysterious Intersection

TLDR: Playground Repro In my application, I'm defining multiple form modules which look roughly like: const firstModule = { name: 'firstModule', mutation: () => {

Solution 1:

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;

Your domain model:

/**
 * Type aliases.
 */typeForms = typeof forms;
typeModule = ValueOf<Forms>;

/**
 * The return type for `getFormConfig`.
 */typeTransformedModule<T extends Module> = Overwrite<T, { mutation: ReturnType<T['mutation']> }>;

The final solution:

exportfunction getFormConfig<K extends keyof Forms>(arg: K) {
  constmodule = forms[arg];

  return ({ ...module, mutation: module.mutation() }) asTransformedModule<Forms[K]>;
}

Usage:

getFormConfig('firstModule').mutation({ variables: { firstModuleArg: 'foo' } })
getFormConfig('secondModule').mutation({ variables: { secondModuleArg: 42 } });

Solution 2:

You could help the compiler with overloads:

functiongetFormConfig(root: 'firstModule'):
    typeof firstModule & { mutation: ReturnType<typeof firstModule.mutation> }
functiongetFormConfig(root: 'secondModule'):
    typeof secondModule & { mutation: ReturnType<typeof secondModule.mutation> }
functiongetFormConfig(root: 'firstModule' | 'secondModule') {
    const rootObj = forms[root];

    const mutation = rootObj.mutation();
    return {...rootObj, mutation}
}

Post a Comment for "Typescript Mysterious Intersection"