这是我遇到的一些奇怪的代码。我已经修复它,但是我不明白为什么为什么正在编译:
const stringOrNull: () => Promise<string | null> = () =>
Promise.resolve(Math.random() < 0.5 ? "Hello, world." : null);
// why does this compile?
const stringOrNullWeird: () => Promise<string> = () =>
new Promise(resolve =>
stringOrNull().then(result => resolve(result ? result : undefined))
);
// will eventually crash with
// (node:20784) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'toLocaleLowerCase' of undefined
[...Array(10).keys()].forEach(async () =>
console.log((await stringOrNullWeird()).toLocaleLowerCase())
);
我通过简单地将返回的Promise
放在一个临时变量中来重写了该函数,因此我可以检查TypeScript推断的类型:它似乎是unknown
,然后 then 函数无法编译。
// does not compile, as expected
// Type '() => Promise<unknown>' is not assignable to type '() => Promise<string>'.
// Type 'Promise<unknown>' is not assignable to type 'Promise<string>'.
// Type 'unknown' is not assignable to type 'string'.ts(2322)
const stringOrNullUnknown: () => Promise<string> = () => {
// const p: Promise<unknown>
const p = new Promise(resolve =>
stringOrNull().then(result => resolve(result ? result : undefined))
);
return p;
};
所以我不明白第二种形式与上面的形式有何不同,为什么上面的一种可以编译,破坏了类型的安全性?
答案 0 :(得分:0)
实际上,原因不是Promise<unknown>
可分配给Promise<string>
。当将new Promise
分配给期望的Promise<string>
所在的位置时,TS会得出new Promise(..)
确实是new Promise<string>(...)
的信息。当像第二个示例一样将其放入变量中时,TS将无处知道类型参数是什么,并且与unknown
一起使用。
允许这样做的真正原因是,这是Promise
构造函数的签名:
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
请注意,对于resolve
,value
是可选的,因此undefined
是可接受的值。 issue表示已解决,但由于here
尽管如此,我还是建议一般避免使用Promise
构造函数:
const stringOrNullWeird: () => Promise<string> = () =>
stringOrNull().then(result => result ? result : undefined);
以上内容比较简单,可能会出错。
或者,如果您的异步逻辑变得过于复杂,请使用async
/ await
:
const stringOrNullWeird: () => Promise<string> = async () => {
const result = await stringOrNull()
return result ? result : undefined;
}
两个都将是错误。调用Promise
构造函数通常是多余的。