zl程序教程

您现在的位置是:首页 >  前端

当前栏目

[!Typescript] Tips: dynamic specify the type of arguments to function

typescript to The of type Function Dynamic Tips
2023-09-14 08:59:11 时间

You can use generics to dynamically specify the number, and type, of arguments to functions.

Here, we create a sendEvent function which only asks for a payload if it's present on the event you're sending.

export type LOGIN = {
      type: "LOG_IN";
      payload: {
        userId: string;
      };
    }
export type SIGNOUT = {
      type: "SIGN_OUT";
    }
export type Event =
  | LOGIN
  | SIGNOUT;

const sendEvent = (eventType: Event["type"], payload?: any) => {};

/**
 * Correct
 */
sendEvent("SIGN_OUT");
sendEvent("LOG_IN", {
  userId: "123",
});

/**Should error */
sendEvent("LOG_IN", {});
sendEvent("LOG_IN");

Above code doesn't have compile error; 

But we want it to has, because "LOG_IN" should has payload, bug "SIGN_OUT" should NOT have payload. It is unsafe to leave the room for error.

 

Tips:

LOGIN is more speicify, {payload: any} is more loosen... type x is true

type LOGIN = {
      type: "LOG_IN";
      payload: {
        userId: string;
      };
    }

type x = LOGIN extends {payload: any} ? true : false // true

 

// Type extends Event["type"] --> "LOG_IN" | "SIGN_OUT"
// Extract<Event, { type: Type } --> will either log LOGIN or SIGNOUT type
// Extract<Event, { type: Type }> extends { payload: infer Payload } --> if the extracted type extends {payload: ...}
// then it we need to require payload as second params
// else we just need type param
const sendEvent = <Type extends Event["type"]>(
  ...args: Extract<Event, { type: Type }> extends { payload: infer Payload }
    ? [type: Type, payload: Payload]
    : [type: Type]
) => {};

 

 

Improved version:

export type LOGIN = {
  type: "LOG_IN";
  payload: {
    userId: string;
  };
};
export type SIGNUP = {
  type: "SIGN_UP";
  payload: {
    username: string;
  };
};
export type SIGNOUT = {
  type: "SIGN_OUT";
};
export type Event = LOGIN | SIGNOUT | SIGNUP;

type SendEventParams<T extends Event["type"]> = Extract<
  Event,
  { type: T }
> extends { payload: infer Payload }
  ? [type: T, payload: Payload]
  : [type: T];
const sendEvent = <T extends Event["type"]>(...args: SendEventParams<T>) => {};
/**
 * Correct
 */
sendEvent("SIGN_OUT");
sendEvent("LOG_IN", {
  userId: "123",
});
sendEvent("SIGN_UP", { username: "wan" });

/**Should error */
sendEvent("LOG_IN", {});
sendEvent("LOG_IN");
sendEvent("SIGN_UP", { errorKey: "wan" });
sendEvent("SIGN_OUT", {});