I'm writing a TypeScript method which takes a generic, T, which extends an array of a mysql result. I map through a method which returns Promise
s of elements of T (T[number]
), and then await them with a Promise.all
. This should result in an array of the elements of T, T[number][]
, which I would have thought would simplify to T
. But when I return my results, I get an error Type 'T[number][]' is not assignable to type 'T'.
What am I doing wrong here? Is T[number][]
not equal to T
?
Here is a simplified ts playground reproduction.
export async function executeAllParallel<
T extends readonly (QueryResult | Record<string | number, unknown>[])[],
>(
dataAccountId: string,
queries: QueryOptions[],
retries: number = 0,
): Promise<T> {
const connection: mysql.PoolConnection =
await poolCluster.getConnection(dataAccountId);
try {
await connection.beginTransaction();
const results: T[number][] = await Promise.all(
queries.map((query) =>
connection.execute<T[number]>(query).then((result) => result[0]),
),
);
await connection.commit();
// Type 'T[number][]' is not assignable to type 'T'.
return results;
} catch (error) {
await connection.rollback();
if (retries <= 1) throw error;
connection.release();
return executeAllParallel(dataAccountId, queries, retries - 1);
} finally {
connection.release();
}
}
TypeScript can express, and I'm assuming you are making use of, tuple types.
A tuple type is another sort of Array type that knows exactly how many elements it contains, and exactly which types it contains at specific positions.
As an example, consider this T
:
type SpecificArray = [number, boolean, string];
This defines an array with exactly three elements of those particular types.
type T1 = T[number];
The best the compiler can do is infer T1 = number | boolean | string
.
type T2 = T1[];
That's (number | boolean | string)[]
, the information on individual element restrictions has been lost.
So no, T[number][]
won't be T
.
Overall, your code assumes the individual queries
will return the types satisfying the corresponding indices of T
, and unless you want to further restrict the types of QueryOptions
, the best you can do is cast results
to T
manually.