Home > Blockchain >  Conditional parameters of types based on strings in TypeScript
Conditional parameters of types based on strings in TypeScript

Time:09-16

I would like to have a method that takes action as first argument, and payload is decided based upon the the first argument.

Let's say I have a method as below:

  async executeUpdateAction<K>(
    action_type: K,
    action_payload: TransferUpdateActions[K],
  ) {
    if (action_type === TRANSFER_AVAILABLE_UPDATE_ACTIONS.CANCEL) {
      return this.cancelTransfer(payload);
    }

    if (action_type === TRANSFER_AVAILABLE_UPDATE_ACTIONS.HOLD) {
      return this.holdTransfer(payload);
    }
   ...
}

This is simply an abstraction that invokes a function based on action type.

Each underlying method (i.e. cancelTransfer, holdTransfer) has its own unique payload requirement.

For instance, lets say holdTransfer requires the following parameters:

  interface HoldPayload {
    duration: string; 
  }

  holdTransfer(payload: HoldPayload) {
    ... 
  }

This being the case, for method executeUpdateAction, if the first argument, action_type is equal to "hold", I want type auto completion so that payload is typed as HoldPayload.

Lastly, I have a wrapper that invokes executeUpdateAction, as below:

interface UpdateDto {
   duration?: string, // payload argument required to execute "Hold" 
   admin_id?: number // payload argument required to execute "Cancel" 
}

update(payload: UpdateDTO) {
   this.executeUpdateAction(payload)
}

So as long as UpdateDTO payload matches ONE of Cancel or Hold Payload, it should be type-safe.

Please advise.

CodePudding user response:

Try this:

type Action = "cancel" | "hold";
type Payload = {
  cancel: {
    userId: string;
  };
  hold: {
    userId: string;
    duration: number;
    amount: number;
  };
};

function executeAction<T extends Action>(action: T, payload: Payload[T]) {
  return;
}


Seems like Typescript doesn't seem to be able to narrow down generics for the second param. In this case you can safely cast your payload.

function executeAction<T extends Action>(action: T, payload: Payload[T]) {
  if (action === "cancel") {
    cancelTransfer(payload as Payload["cancel"]);
  }
}
  • Related