Sword fish
Sword fish

Reputation: 51

How can I define an array type in typescript that can connect function and its parameter?

Here I have three functions and I put them into a literal:

type AnyCallback = (...args: any) => unknown;
const callbackAlpha = (data: string) => {
  console.log(data);
  return data.length;
};

const callbackBeta = (data: string, prefix: string) => {
  console.log(data, prefix);
  return prefix + data;
};

const callbackGamma = (data: string, prefix: string, suffix: string) => {
  console.log(data, prefix, suffix);
  return prefix + data + suffix;
};

let allCallBack: Record<string, AnyCallback> = {
  callbackAlpha,
  callbackBeta,
  callbackGamma,
};

and then, I want to define an array type to record the function name and parameters when I call it, the member supposed to be like this:

{
   name: //name of a function in the literal
   args: //parameters that needed when I call the function
}

Firstly, I tried like this:

type TaskType = keyof typeof allCallBack;
type TaskQueue = {
  name: TaskType;
  args: Parameters<(typeof allCallBack)[TaskType]>;
}[];

Obviously, it is wrong, because you can give "name" any string value,and "args" can be any parameters of the three functions.

Upvotes: 1

Views: 53

Answers (1)

Boopathi Rajaa
Boopathi Rajaa

Reputation: 4729

In TypeScript, when you assign/alias a subtype (a more specific type / narrow type) to a supertype (a less specific type / broader type), the subtype is not preserved in the supertype.

For example,

let a: "foo" = "foo";

let b: string = a;

// typeof b === string and not "foo"

The same thing has happened to your functions when you assign subtypes "callbackAlpha", (data: string) => number to its corresponding supertypes - string, and (...args: any[]) => any as defined in Record<string, AnyCallback>

If you want TS to preserve the exact type information, then removing the type declaration (Record) for allCallback would help -

let allCallback = {
  callbackAlpha,
  callbackBeta,
  callbackGamma,
}

Here, the type inference works just fine and the exact types are preserved. And then, to construct the TaskQueue type preserving the relation/association between the name and args, it can be done using generics -

type TaskType = keyof typeof allCallBack;
type TaskQueueItem<Name extends TaskType> = {
    name: Name,
    args: Parameters<(typeof allCallBack)[Name]>
}
type TaskQueue = TaskQueueItem<TaskType>[];

For example,

const myAlphaName = "callbackAlpha" as const;
const myBetaName = "callbackBeta" as const;
const myAlphaTask: TaskQueueItem<typeof myAlphaName> = {
    name: myAlphaName,
    args: ["a"]
};
const myBetaTask: TaskQueueItem<typeof myBetaName> = {
    name: myBetaName,
    args: ["a", "b"]
};
const myAllTasks: TaskQueue = [myAlphaTask, myBetaTask];

TS Playground link

Upvotes: 1

Related Questions