Home > Back-end >  Typings of a return object with computed property names
Typings of a return object with computed property names

Time:10-02

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>;
}

Playground link

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

Playground link

  • Related