在 TypeScript 中,为什么异步函数不能返回 T 类型的联合 | 答应<T>?

D. *_*ick 5 promise async-await typescript union-types

我正在用打字稿构建一个 API,一些控制器操作可以同步,而另一些则不能。我想指定一个响应类型,如下所示:

type ActionResult =IHttpActionResult | Promise<IHttpActionResult>;
Run Code Online (Sandbox Code Playgroud)

然后,当我构建操作时,当它们变得基于承诺时,我可以只添加异步并完成它。

但是,打字稿抱怨“异步函数或方法的返回类型必须是全局 Promise 类型”。

为什么异步函数不能返回 的并集T | Promise<T>

这是一个例子:

type StringPromise = Promise<string>;

// These two work as you'd expect
async function getHello(): Promise<string> {
    return 'hello';
}

async function getGoodbye(): StringPromise {
    return 'goodbye';
}

type StringyThingy = string | Promise<string>;

// the next two work as you would expect them to
function getHoorah(): StringyThingy {
    return 'hoorah!';
}

function getWahoo(): StringyThingy {
  return new Promise(resolve => resolve('wahoo'));
}

// This one results in the error:
// "the return type of an async function or method must be the global Promise type."
async function getSadface(): StringyThingy {
  return ':(';
}    
Run Code Online (Sandbox Code Playgroud)

以下是上述代码的一些示例输出:

getHello().then(console.log);
getGoodbye().then(console.log);
console.log(getHoorah());

// The library I'm using is probably using typeguards for this
// I'd imagine
const wahoo = getWahoo();
if (typeof(wahoo) === 'string') {
  console.log(wahoo);
} else {
  wahoo.then(console.log);
}
Run Code Online (Sandbox Code Playgroud)

k0p*_*kus 5

async符号是语法糖:“此函数将始终返回一个承诺。”

即使你这样声明:

const foo = async() => 3;
Run Code Online (Sandbox Code Playgroud)

它基本上与以下相同(尽管更严格):

const foo = () => new Promise(resolve => resolve(3));
Run Code Online (Sandbox Code Playgroud)

或作为:

const foo = () => Promise.resolve(3);
Run Code Online (Sandbox Code Playgroud)

所有这些示例都会返回一个 Promise。

主要区别在于,“普通”函数可以返回 Promise 和其他类型,但一旦async使用它,它总是会返回一个 Promise。

即使 Promise 立即解析,函数也无法按照设计返回 Promiseasync

你必须等待它/然后使用它。

mozilla 的 JavaScript 参考中关于 async 关键字的内容也有说明:

async 函数声明定义了一个异步函数,该函数返回一个 AsyncFunction 对象。异步函数是通过事件循环异步操作的函数,使用隐式 Promise 返回其结果。但使用异步函数的代码的语法和结构更像使用标准同步函数。

特别是关于返回类型:

一个 Promise,将使用异步函数返回的值进行解析,或者通过从异步函数内部抛出未捕获的异常来拒绝。


考虑到这一点,我建议您将 API 设置async为默认值。如果你的某些行为是同步的,对于外界来说并不重要。在这种情况下,您可以立即解决承诺。不需要你的type StringyThingy = string | Promise<string>;

输入 againstPromise<string>并让 async 为您处理包装到 Promise 中,或者在真正的异步用例中实际返回其他 Promise。这样您就不必检查 Promise 的实例,但您将以相同的方式处理异步/同步分支。


如果你真的想要union类型(我真的不推荐这个),那么你就必须放弃关键字的使用async

您可以定义返回任一类型的普通函数:

const foo = (x:number): Promise<number>|number => {
    if(x >=0) {
         return new Promise(resolve => resolve(x));
    } else {
         return x;
    }
}
Run Code Online (Sandbox Code Playgroud)