我正在尝试创建一个仅允许返回非原始结果的类型化函数。同步或异步。但是,似乎Promise<object>
约束未在代码中强制执行,因为Promise
本身已经是非原始的。
function nonPrimitiveResult(): object | Promise<object> {
return {}; // OK
return 1; // Error
return undefined; // Error
return Promise.resolve({}); // OK
return Promise.resolve(1); // OK, why?
return Promise.resolve(); // OK, why?
}
答案 0 :(得分:0)
虽然可能不是完美的解决方案,但您可以使用条件类型对行为类型进行建模。条件类型的顺序很重要,因为在检查其他类型的Promise之前,我们将Promise<Primitive>
丢弃了。
您可以通过在条件类型中添加更多分支来覆盖不需要的场景,从而使该示例更加严格。
不幸的是,为了使其正常工作,return语句需要尝试将类型转换为通用类型:
type Primitive = number | string | boolean;
type X<T> = T extends Primitive ?
never
: T extends Promise<Primitive> ?
never
: T extends Promise<any> ?
Promise<T>
: T;
const nonPrimitiveResult = <T>(): X<T> => {
return 1 as X<T>; // error
return true as X<T>; // error
return "hello world" as X<T>; // error
return Promise.resolve(1) as X<T>; // error
return Promise.resolve(true) as X<T>; // error
return Promise.resolve("hello world") as X<T>; // error
return Promise.resolve() as X<T>; // error
return {} as X<T>; // ok
return Promise.resolve({}) as X<T>; // ok
}
答案 1 :(得分:0)
您想说“要么根本不是object
的{{1}},要么是Promise
”。不幸的是,“不是Promise<object>
的{{1}}这个概念最容易表达为subtraction type,而TypeScript目前不支持这种概念。
一种继续进行操作的方法是描述一个绝对不是object
的对象类型,并且包括您可能希望允许的 most 个非基元,而不允许所有它们。例如,可以合理地预期,如果Promise
的返回值包含一个名为Promise
的属性,则结果为nonPrimitiveResult()
。或等效地,如果结果为 not then
,则 not 包含名为Promise
的属性。如果这是一个合理的折衷方案,则可以将Promise
更改为then
。类型object | Promise<object>
的意思是“这是一个没有定义的{then?: never} | Promise<object>
属性的对象”。
在这种情况下,您的签名将变为:
{then?: never}
,您的所有案例的行为均符合您的要求。当然,这是一种解决方法。如果实际上“具有then
属性的结果必须是function nonPrimitiveResult(): { then?: never } | Promise<object> {
return {}; // OK
return 1; // Error
return undefined; // Error
return Promise.resolve({}); // OK
return Promise.resolve(1); // Error
return Promise.resolve(); // Error
}
的语句不适用,那么最终将禁止有效对象:
then
因此,这不是一个完美的解决方案。您可以变得更加聪明,并开始描述一个更紧密地表示“不是Promise
的对象”的类型的并集,例如:
function nonPrimitiveResult(): { then?: never } | Promise<object> {
return {then: "theDinosaursCame"}; // Error, uh oh
}
但是,这开始变得越来越复杂,边际收益也越来越少。因此,除非遇到用例使其无法使用的情况,否则我可能只会坚持使用Promise
。
无论如何,希望能有所帮助。祝你好运!
答案 2 :(得分:0)
您知道,JS(因此还有TypeScript)中的非基元包括object
(和array
,后者仍然是幕后对象)。
所以回答您问题的最直接的方法是:
async function nonPrimitiveResult(): Promise<object> {
// ... implementation
}
作为对该答案的解释,让我们深入研究为什么在IDE中会得到奇怪的结果。
解释为什么看到自己看到的东西
之所以得到一个奇怪的假阳性,是因为您没有将该函数标记为async
。如果您这样做的话,事情将会变得更加清楚。
async function nonPrimitiveResult(): Promise<object> {
return Promise.resolve(1); // Type 'number' is not assignable to type 'object'.
}
更多上下文:
1)为什么要始终将每个返回promise的函数标记为异步
then
函数的对象。因此,我建议您使用tslint
帮助您始终等待返回承诺的函数。具体来说,您需要的是promise-function-async
规则:https://palantir.github.io/tslint/rules/promise-function-async/ 2)返回对象或诺言
async
后,您将发现您不再能将响应类型表示为object | Promise<object>
,因为会出现以下错误:
The return type of an async function or method must be the global Promise<T> type.