I think the following will be the most fitting solution
type Merged<SingleKeys extends string, TupleKeys extends string, valueType = string> = {
[K in SingleKeys | TupleKeys]: K extends SingleKeys ? valueType : valueType[];
};
type Speech = {
mySpeech: string
}; // Just for the example
const getSpeech = async (text: string): Promise<Speech> => {
return new Promise(resolve => {
resolve({ mySpeech: text })
});
};
async function bulkGetSpeech<SingleKeys extends string, TupleKeys extends string>(
texts: Merged<SingleKeys, TupleKeys>
): Promise<Merged<SingleKeys, TupleKeys, Speech>> {
const keys = Object.keys(texts) as (keyof typeof texts)[];
const promises = keys.map(key => {
const value = texts[key];
if (Array.isArray(value)) {
const tupleKey = key as keyof TupleKeys;
return Promise.all(value.map(text => getSpeech(text))).then(values => [tupleKey, values]);
}
const singleKey = key as keyof SingleKeys;
return getSpeech(value).then(audio => [singleKey, audio]);
});
const results = await Promise.all(promises);
return Object.fromEntries(results);
}
const texts: Merged<"a" | "b", "c"> = {
a: "a",
b: "b",
c: ["c1", "c2"]
};
let speeches: Merged<"a" | "b", "c", Speech>;
bulkGetSpeech(texts).then(val => { speeches = val });