Andrei V
Andrei V

Reputation: 7506

Using Mapped Types to map an array of generic types to a different (return) array

I have a function which takes a variable number of input parameters and returns an array of the same length, with the items having the generic types specified when the function is called. It's actually a function which returns a Promise array.

Using mapped types I did this:

export const Service = {
   /* ... */
   promisify: <T extends [] = any>(...p: Array<string>): { [P in keyof T]: Promise<T[P]> } => {
      const result = [];
      /* ... */
      return result;
   }
}

but it's giving an error because an object is declared as the return type...

What I'm trying to achieve is someting allong these lines:

/*pseudocode*/
export const Service = {
   promisify: <T extends [] = any>(...p: Array<string>): [ [P in keyof T]: Promise<T[P]> ] => {
      const result = [];
      /* ... */
      return result;
   }
}

The result I'm after is being able to call this function and have the right return type:

Service.promisify<number>("val1"); //=> [Promise<number>]
Service.promisify<number, string>("val1", "val2"); //=> [Promise<number>, Promise<string>]
Service.promisify<number, string, MyType>("val1", "val2", "val3"); //=> [Promise<number>, Promise<string>, Promise<MyType>]

Is this currently possible using mapped types or do I need to specifiy overloads?

Upvotes: 1

Views: 243

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249646

Your general idea should work, you just have a couple of issues around syntax. There is no mapped tuple syntax, but regular mapped types work with tuples as expected (since 3.1 I believe).

Also you need to constrain T to be an array type, and you will need to pass in the types you want to map as a tuple:

export const Service = {
   promisify: <T extends any[]>(): { [P in keyof T]: Promise<T[P]> } => {
      const result = [] as any as { [P in keyof T]: Promise<T[P]> };
      /* ... */
      return result;
   }
}

Service.promisify<[number]>(); //=> [Promise<number>]
Service.promisify<[number, string]>(); //=> [Promise<number>, Promise<string>]
Service.promisify<[number, string, MyType]>(); //=> [Promise<number>, Promise<string>, Promise<MyType>]

Upvotes: 1

Romain Deneau
Romain Deneau

Reputation: 3061

promisify() returns an object. If you want to return an object, initialize it with {} and define its type with as MyType, the method return type will be inferred. I also prefer A[] (more Typescript way) instead of Array<A> (more C#/Java way):

  promisify: <T extends [] = any>(...p: string[]) => {
    const result = {} as { [P in keyof T]: Promise<T[P]> };
    /* ... */
    return result;
  }

Upvotes: 0

Related Questions