My expectation is to automatically determine the return type of async functions when they are yielded as values from a generator.
In the following example, the inference of the type of the yielded async functions appears to work correctly (detected as () => Promise<string>
). However, the variable T
cannot be inferred as string
and ends up being unknown
.
I have simplified a complex issue to the example shown below (refer to playground). This example includes all the necessary features for the actual problem I am facing.
The commented code provides additional information about the need to infer both J and T. Importantly, extensions to Job<T>
can contain job-specific metadata.
Could someone figure out how to construct type definitions that can successfully infer both J
and T
in the given scenario, so that the settlement type becomes
JobSettlement<string, () => Promise<string>>
instead of JobSettlement<unknown, () => Promise<string>>
https://i.sstatic.net/ZMN04.png
I suspect this is one of the unresolved cases mentioned in microsoft/TypeScript#47599, but I'm unsure how to reconfigure it so TypeScript can handle the inference.
type Job<T> = () => Promise<T>
export interface JobFulfilment<T, J extends Job<T>> {
job: J;
status: "fulfilled";
value: T;
}
export interface JobRejection<T, J extends Job<T>> {
job: J;
status: "rejected";
reason: unknown;
}
export type JobSettlement<T, J extends Job<T>> =
| JobFulfilment<T, J>
| JobRejection<T, J>;
export async function* createSettlementSequence<T, J extends Job<T>>(
createJobSequence: () => AsyncGenerator<J>
): AsyncIterable<JobSettlement<T, J>> {
for await (const job of createJobSequence()){
try{
const value = await job();
yield {
job,
value,
status:"fulfilled",
}
}
catch(reason){
yield {
job,
reason,
status:"rejected"
}
}
}
}
const settlementSequence = createSettlementSequence(async function* () {
yield async () => "foo"
yield async () => "bar"
yield async () => "baz"
})
// const settlementSequenceWithMetadata = createSettlementSequence(async function* () {
// yield Object.assign(async () => "foo", { task: 0})
// yield Object.assign(async () => "bar", { task: 1})
// yield Object.assign(async () => "baz", { task: 2})
// })