TS 4.0 allows spreading tuple types and labelled tuple types.
I'm attempting to use both features to create a sort of with-context function or bracketing pattern.
Here is my attempt:
type Resource<T = void> = () => Promise<[
release: () => Promise<void>,
resource: T
]>;
async function withF<
Resources extends Array<Resource<unknown>>,
Result
>(
resources: Resources,
f: (...resources: [...Resources]) => Promise<Result>
): Promise<Result> {
const releases = [];
const items = [];
try {
for (const resource of resources) {
const [release, item] = await resource();
releases.push(release);
items.push(item);
}
return await f(...items);
} finally {
releases.reverse();
for (const release of releases) {
await release();
}
}
}
The idea is that you can use it like this:
let count: number = 0;
await withF(
[
async () => {
++count;
return [async () => { --count; }, count];
}
],
async (c: number) => {
return c;
}
);
The problem is that the types don't match because in my:
f: (...resources: [...Resources]) => Promise<Result>
The Resources
extends Array<Resource<unknown>>
, and I want to say that f
takes a spread of the second element for each return type promise of Resources
.
First challenge is how to do a mapping type into Resources
. It seems it should be possible with https://devblogs.microsoft.com/typescript/announcing-typescript-3-1/#mappable-tuple-and-array-types.
The second step is to apply the indexing option. Which should work in the mapping type as well. But again I'm not sure how to do this.
Ideally we want some sort of type constructor that does it:
f: (...resources: [...TC<Resources>]) => Promise<Result>
Where TC
is a special type constructor that maps Resources
to the 2nd element of each return type and still preserves the tuple length & order.
Further attempts for mapping into a tuple of functions:
type Functions = ((...args: any) => unknown)[];
type FunctionReturns<T extends [...Functions]> = { [K in keyof T]: ReturnType<T[K]> };
const fs: Functions = [() => 1, () => 'abc'];
type FsReturns = FunctionReturns<typeof fs>;
For whatever reason, even though basic ability to map into tuple types work, the ReturnType
here still complains even though we've said that T
extends an array of functions. It seems that ReturnType
doesn't seem to work when attempting to map into tuple types.