Stedi-Public-Archive / ts2asl

TypeScript to AWS ASL transpiler
Apache License 2.0
149 stars 4 forks source link

bug: asl.map produces return type of Promise to array of Promises #910

Open dmeehan1968 opened 12 months ago

dmeehan1968 commented 12 months ago

Example code

const migratedItems = await asl.map({
    items: scanResult.Items,
    iterator: async (item) => {
        return await migrateItem(item)
    },
})

If migrateItem() is defined as migrateItem(item: Record<string, AttributeValue>): Promise<Record<string, AttributeValue>> {...}, then the return type from asl.map is Promise<Promise<Record<string, AttributeValue>>[]> (promise for array of promises). In the example code, another await would be required to resolve back to the actual resolved value.

It seems that the definition of Map<Input,Output>'s iterator is incorrect in not mentioning Promise. The following definition produces the correct output (paraphrased) whilst allowing the iterator to be async or not.

interface Map<I,O> { 
    items: I[], 
    iterator: (item: I) => Promise<O> | O
    //...
}

const map = async <I,O>(args: Map<I,O): Promise<O[]> => {
    return {} as O[]
}

It may be possible to force the correct type using coercion (as a workaround):

const migratedItems = (await asl.map({
    items: scanResult.Items,
    iterator: async (item) => {
        return await migrateItem(item)
    },
})) as unknown as Record<string, AttributeValue>[]
dmeehan1968 commented 12 months ago

I realised that I should probably be using Promise.all (if I was writing typescript) but this produces an error:

        const migratedItems = await Promise.all(await asl.map({
            items: scanResult.Items,
            iterator: async (item) => {

                return await migrateItem(item)

            },
        }))

Error:

    Error: promise.all statement must have array literal expression as argument

    Kind: CallExpression
    Source: Promise.all(await asl.map({
                items: scanResult.Items,
                iterator: async (item) => {

                    return await migrateItem(item)

                },
            }))

This seems at odds with:

await asl.sdk(DynamoDB).batchWriteItem({
    parameters: {
        RequestItems: {
            'MyTableName': await Promise.all(scanResult.Items.map(async (item) => ({
                PutRequest: {
                    Item: await migrateItem(item),
                }
            })))
        }
    }
})

Note #911 prevents use of a computed table name.

The difference between asl.map and Array.map is that the former allows for concurrency control via the maxConcurrency property, whilst the latter uses Map defaults, which is unconstrained concurrency. Not being able to wrap asl.map in Promise.all means that there is no option to control concurrency.