Reputation: 51
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
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];
Upvotes: 1