Búsqueda de sitios web

typescript - Convertir un array de promesas en una sola promesa que podría ser uno de los tipos incluidos: [P<string>, P<number>] -> Promise<string | number>


Estoy intentando crear una función que devuelva el primer valor resuelto de un arreglo de Promesas

const firstSettledValue = (...promises: Array<Promise<any>>): Promise<any> {
   // ...
}

const v = firstSettledValue(
  new Promise<string>(),
  new Promise<number>(),
  new Promise<boolean>()
)

v // Promise<string | number | boolean>

Respuesta #1

Puedes simplemente usar Promise.race():

const v = Promise.race([
  new Promise<string>(() => {}),
  new Promise<number>(() => {}),
  new Promise<boolean>(() => {})
]);

v // Promise<string | number | boolean>

El tipo de retorno se infiere correctamente.

Respuesta #2

Podemos usar los parámetros rest genéricos de TypeScript para manejar esto con precisión.

Esto nos permite declarar un tipo de tupla genérico que captura el tipo individual de cada argumento cuando se llama a la función.

Vamos a desglosarlo.

Primero capturaremos el tipo de cada argumento pasado a la función

const firstSettledValue = <P extends Promise<any>[]>(...promises: P) => {}

const v = firstSettledValue(
  Promise.resolve('Hello World'),
  Promise.resolve(15),
  Promise.resolve(false)
)

Ahora, podemos obtener un tipo unión de cada uno de todos los argumentos, es decir, los elementos de P, mediante P[number].

type U = P[number] // string | number | boolean

Tenga en cuenta que en lo anterior P[0] es del tipo string, P[1] es del tipo number, y así sucesivamente.

Sin embargo, no podemos usar P[Number] directamente en el código anterior porque nuestros elementos son promesas y necesitamos extraer sus tipos de valor.

Para hacer eso, podemos introducir un pequeño tipo auxiliar

type ValueType<T> = T extends Promise<infer U>
  ? U
  : T;

Dado un Promise<T>, el auxiliar anterior produce T.

Finalmente componemos estas técnicas juntas

const firstSettledValue = <P extends Promise<any>[]>(...promises: P): Promise<ValueType<P[number]>> => {
  const randomIndex = Math.floor(Math.random() * 100) % promises.length;
  return promises[randomIndex];
};

const v = firstSettledValue(
  Promise.resolve('Hello World'),
  Promise.resolve(15),
  Promise.resolve(false)
);

type ValueType<T> = T extends Promise<infer U>
  ? U
  : T;

Enlace del parque

Y hemos terminado. v tiene tipo Promise<string | number | boolean>, basado en las promesas especificadas en la llamada.