vietzky
vietzky

Reputation: 21

Flowtype generic function with different parameters

I'm struggling with flowtype declaration for a generic function with different pairs of parameters.

My goal is to have a function which return an object of certain union type depending on input parameters. I'm having a big load of messages that i want to type (for this example i'm using only two)

type Message1 = {
  event: 'UI',
  type: 'receive',
  payload: boolean
}

type Message2 ={
  event: 'UI',
  type: 'send',
  payload: {
    foo: boolean;
    bar: string;
  }
}

type MessageFactory<T> = (type: $PropertyType<T, 'type'>, payload: $PropertyType<T, 'payload'>) => T;

export const factory: MessageFactory<Message1> = (type, payload) => {
    return {
      event: 'UI',
      type,
      payload
    }
}

factory('receive', true);
// factory('send', { foo: true, bar: "bar" });

when i change

MessageFactory<Message1> 

to

MessageFactory<Message1 | Message2>

it will throw an error

Could not decide which case to select. Since case 1 [1] may work but if it doesn't case 2 [2] looks promising too. To fix add a type annotation to `payload` [3] or to `type` [4]

You can ty it here

any idea how to declare this function?

or is it stupid idea and i'm going to the wrong direction?

any better solutions?

Upvotes: 2

Views: 256

Answers (1)

James Kraus
James Kraus

Reputation: 3478

Create a GenericMessage with type parameters for your desired properties (type and payload), then have your factory return a GenericMessage:

(Try)

type GenericMessage<TYPE: string, PAYLOAD> = {
  event: 'UI',
  type: TYPE,
  payload: PAYLOAD
}

const factory = <T: string, P>(type: T, payload: P): GenericMessage<T, P> => {
    return {
      event: 'UI',
      type,
      payload
    }
}

const test1 = factory('receive', true);
const test2 = factory('send', { foo: true, bar: "bar" });

// Let's check the new type against Message1 and Message2:
type Message1 = {
  event: 'UI',
  type: 'receive',
  payload: boolean
}

type Message2 ={
  event: 'UI',
  type: 'send',
  payload: {
    foo: boolean;
    bar: string;
  }
}

// Type assertions
(test1: Message1);
(test2: Message2);
(test1: Message2); // Error!

If you want, you can create a MessageFactory type that returns a GenericMessage<T, P>. You can also create an EVENT type parameter if you need to control the event property on the object.

(You don't need to call it GenericMessage, I just called it that to make a distinction between your existing types and this new one)

Upvotes: 0

Related Questions