Typescript - dynamisk array av löften med olika returvärden


Kan någon hjälpa mig att lösa vad som är fel med den här koden? Jag kan inte hitta rätt typ att använda i det där Promise.all-anropet längst ner. Jag försökte också Promise.all<Services[], PullRequests[]>(ops) men PullRequests[] kan inte vara valfritt...

function wait<Twait>(time: number, response: Twait): Promise<Twait> {
    return new Promise(resolve => setTimeout(() => resolve(response), time))
}

interface Service {
    name: string;
    id: number;
}

async function getServices(): Promise<Service[]>  {
    const response = await wait(400, { "id": 200 });
    return [{ name: "service", id: response.id }]
}


interface PullRequest {
    prType: string;
    githubId: number;
}

async function getPrs(): Promise<PullRequest[]>  {
    const response = await wait(400, { "githubId": 200 });
    return [{ prType: "foo", githubId: response.githubId }]
}


async function main(): Promise<void> {

    const ops: [ PromiseLike<Service[]>, PromiseLike<PullRequest[]>? ] = [getServices()]

    if (Math.random() > 0.5) { // <== this is random on purpose.
        ops.push(getPrs())
    }
    
    
    const [ services, prs ] = await Promise.all(ops) // This throws a ts compile error (attached below)
    console.log("services:")
    console.log(services)
    console.log("prs:")
    console.log(prs)    
}

No overload matches this call. The last overload gave the following error.
 Argument of type '[PromiseLike<Service[]>, (PromiseLike<PullRequest[]> | undefined)?]' is not assignable to parameter of type 'Iterable<Service[] | PromiseLike<Service[] | undefined> | undefined>'. 
The types returned by '[Symbol.iterator]().next(...)' are incompatible between these types. 
Type 'IteratorResult<PromiseLike<Service[]> | PromiseLike<PullRequest[]> | undefined, any>' is not assignable to type 'IteratorResult<Service[] | PromiseLike<Service[] | undefined> | undefined, any>'. 
Type 'IteratorYieldResult<PromiseLike<Service[]> | PromiseLike<PullRequest[]> | undefined>' is not assignable to type 'IteratorResult<Service[] | PromiseLike<Service[] | undefined> | undefined, any>'. 
Type 'IteratorYieldResult<PromiseLike<Service[]> | PromiseLike<PullRequest[]> | undefined>' is not assignable to type 'IteratorYieldResult<Service[] | PromiseLike<Service[] | undefined> | undefined>'. 
Type 'PromiseLike<Service[]> | PromiseLike<PullRequest[]> | undefined' is not assignable to type 'Service[] | PromiseLike<Service[] | undefined> | undefined'. 
Type 'PromiseLike<PullRequest[]>' is not assignable to type 'Service[] | PromiseLike<Service[] | undefined> | undefined'. 
Type 'PromiseLike<PullRequest[]>' is not assignable to type 'PromiseLike<Service[] | undefined>'. 
Type 'PullRequest[]' is not assignable to type 'Service[]'. 
Type 'PullRequest' is missing the following properties from type 'Service': name, id

Svar #1

Ser ut att vara ett känt problem i TS-biblioteksdefinitioner, enligt microsoft/TypeScript#28427. För närvarande hårdkodar biblioteket en massa specifika tuppeltyper som inte inkluderar valfria element. Det har gjorts vissa försök att åtgärda detta, men uppenbarligen är PromiseLike-typer och hur de interagerar med await tillräckligt komplicerade för att ingen av de pull requests som åtgärdar detta har accepterats än. En kommentar i microsoft/TypeScript#39788 förklarar att en ändring kan bryta befintlig kod i verkliga världen som är beroende av de nuvarande definitionerna.

Tills detta åtgärdas uppströms, kan du använda deklarationssammanfogning för att lägga till din egen signatur för Promise.all() som bara kartlägger promises upplösning över elementen i den passerade arrayen eller tuplen:

interface PromiseConstructor {
  all<T extends readonly any[]>(
    values: { [K in keyof T]: T[K] | PromiseLike<T[K]> }
  ): Promise<T>;
}

Och du kan testa att det gör det du vill:

const promiseAllOps = Promise.all(ops); // no error now  
// const promiseAllOps: Promise<[Service[], (PullRequest[] | undefined)?]>
const awaitPromiseAllOps = await promiseAllOps;
// const awaitPromiseAllOps: [Service[], (PullRequest[] | undefined)?]
const [services, prs] = awaitPromiseAllOps;
services; // Service[]
prs; // PullRequest[] | undefined

Lekplatslänk