Search code examples
typescriptconditional-types

Union-Type turns into Intersection-Type


Problem

I want to create a factory that delivers different functions depending on the given parameters. This can be solved with conditional types.

type DispatchConditional<TPayload> = TPayload extends undefined
  ? () => void
  : (payload: TPayload) => void;

The type DispatchCondition can be instrumented by an interface.

interface ActionDispatcher<TPayload> {
  dispatch: DispatchConditional<TPayload>;
}

If I build a factory based on the definitions above, it will break if a Union-Type is used. Somehow the union-type defined by payloadUnion() becomes an Intersection-Type.

enter image description here

Question

What adjustments do I have to make to produce a type definition for the method dispatch that supports union-types?

Playground


Solution

  • DispatchConditional is a distributed conditional type. This means that DispatchConditional<number | number[]> is equivalent to DispatchConditional<number> | DispatchConditional<number[]> which in turn will be a union of functions. And a union of functions is only callable with an intersection of the parameter type (since we don't know which function we actually get at runtime, we have to provide parameters that work with BOTH function signatures, resulting in the intersection)

    The simple solution is to disable the distributive behavior, by wrapping the type parameter in a tuple type ( distribution only happens over naked type parameters):

    
    type DispatchConditional<TPayload> = [TPayload] extends [undefined]
      ? () => void
      : (payload: TPayload) => void;
    

    Playground Link