在TypeScript中,为什么异步函数不能返回类型T的并集| Promise <T>?

时间:2019-08-28 15:42:30

标签: typescript promise async-await union-types

我正在用打字稿构建API,一些控制器动作可以同步,而其他控制器则不能。我想这样指定响应类型:

type ActionResult =IHttpActionResult | Promise<IHttpActionResult>;

然后,当我在构建动作时,当它们成为基于诺言的动作时,我可以在异步之前添加并完成它。

但是,打字稿抱怨“异步函数或方法的返回类型必须是全局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 ':(';
}    

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

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);
}

1 个答案:

答案 0 :(得分:3)

async表示法是:

此功能将始终返回承诺

即使您这样声明:

const foo = async() => 3;

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

const foo = () => new Promise(resolve => resolve(3));

或作为:

const foo = () => Promise.resolve(3);

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

主要区别在于,“常规”函数可以返回Promise和其他类型,但是一旦使用async,它总是将返回承诺。

即使一个承诺立即解决,async函数也不会通过设计不返回承诺

您将不得不等待/使用它。

这在mozilla's JavaScript reference about the async keyword上也有说明:

  

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

特别是关于返回类型:

  

一个Promise,它将使用异步返回的值来解决   函数,或因内部抛出未捕获的异常而被拒绝   异步功能。


考虑到这一点,建议您将API async设置为默认值。如果您的某些动作是同步的,那么与外界无关紧要。在这种情况下,您可以立即兑现承诺。不需要您的type StringyThingy = string | Promise<string>;

针对Promise<string>进行键入,然后让异步处理包装成您的承诺,或者在实际的异步用例中实际返回其他承诺。这样,您不必检查promise的实例,但是您将以相同的方式处理两个异步/同步分支。


如果您真的想要联合体类型(我真的不建议这样做),那么您必须放弃使用async关键字。

您可以定义返回两种类型的普通函数:

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