I want to write a function like this one:
function createFunctions(name:string) {
return {
[`${name}Sender`]: function Sender() {},
[`${name}Receiver`]: function Receiver() {},
}
}
The return type of this function is
{
[x: string]: () => void;
}
Is it possible to get the typings here right? Such that the return type of createFunctions('Message')
would be:
{
MessageSender: () => void
MessageReceiver: () => void
}
?
CodePudding user response:
(Please see the warning at the end.)
You can create a mapped type to define the return type of the function:
type CreateFunctionsResult<Name extends string> = {
[key in `${Name}Sender` | `${Name}Receiver`]: () => void;
};
Then apply that as the function return type, making the function generic:
function createFunctions<Name extends string>(name: Name): CreateFunctionsResult<Name> {
return {
[`${name}Sender`]: function Sender() {},
[`${name}Receiver`]: function Receiver() {},
} as CreateFunctionsResult<Name>;
}
Unfortunately, I can't avoid that type assertion on the actual object. And thanks to jcalz, I think I understand why: unions.
Beware: The above will won't work correctly if Name
is a union:
// `prefix` has a union type
const prefix = Math.random() < 0.5 ? "Message" : "SomethingElse";
// ^? const prefix: "Message" | "SomethingElse"
const x = createFunctions(prefix);
// All four of these exist as far as the type is concerned, but of course `prefix`
// will only be one or the other!
x.MessageSender();
// ^? (property) MessageSender: () => void
x.MessageReceiver();
// ^? (property) MessageReceiver: () => void
x.SomethingElseSender();
// ^? (property) SomethingElseSender: () => void
x.SomethingElseReceiver();
// ^? (property) SomethingElseReceiver: () => void