Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

  • TypeScript Playground
like image 796
Gregor Woiwode Avatar asked Sep 11 '25 12:09

Gregor Woiwode


1 Answers

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

like image 86
Titian Cernicova-Dragomir Avatar answered Sep 14 '25 05:09

Titian Cernicova-Dragomir