我只花了大约一个小时来调试代码中的类型错误,最终将其归结为:
function foo(): Promise<string> {
return new Promise((resolve, reject) => {
resolve();
});
}
我很失望Typescript没有完成其一项工作并检测到此错误。为什么不呢?
答案 0 :(得分:2)
很遗憾,这是TypeScript design limitation (see microsoft/TypeScript#22040)。 Promise
构造函数的构造签名的standard library's typing为:
new <T>(executor: (
resolve: (value?: T | PromiseLike<T>) => void,
reject: (reason?: any) => void
) => void): Promise<T>;
您可以看到resolve()
的自变量被声明为optional,无论undefined
是否可分配给类型T
。如果您想使用Promise<void>
,Promise<undefined>
或Promise<string | undefined>
,或者undefined
是有效的T
的任何东西,那很好,因为{ 1}}与resolve()
类似。但是,正如您所注意到的,如果您想要resolve(undefined)
,那么不幸的是Promise<string>
被接受了。
可以通过调整标准库的类型来解决,但是不幸的是,previous attempts to fix this (see microsoft/TypeScript#22772)导致太多breaking changes in existing TypeScript code,因此他们并不想将此类更改实际推向上游。太糟糕了。
幸运的是,如果要在自己的代码中解决此问题,可以使用declaration merging添加优先考虑的构造函数签名重载。您使用的构造函数签名的特定版本取决于您的用例。如果您愿意,可以始终禁止零参数resolve()
并要求人们写resolve()
(如果他们想要的话)
resolve(undefined)
(请注意,declare global { // comment out this line if not in a module
interface PromiseConstructor {
new <T>(executor: (
resolve: (value: T | PromiseLike<T>) => void, // value is not optional
reject: (reason?: any) => void) => void
): Promise<T>;
}
} // comment out this line if not in a module
在全局范围内,因此,如果代码在模块中,则需要使用global augmentation with declare global
才能使以上内容起作用。如果您已经在全局范围内,则应省略PromiseConstructor
块。)
或者,您可以尝试变得更加聪明,可以编写签名的版本,该签名的版本仅当declare global
可分配给resolve()
时允许不带参数的undefined
:
T
让我们使用后一种类型并检查某些示例代码的行为:
declare global {
type PossiblyOptional<F extends (a: any) => any> = F extends (...a: infer A) => infer R ?
undefined extends A[0] ? (...a: Partial<A>) => R : F : never;
interface PromiseConstructor {
new <T>(executor: (
resolve: PossiblyOptional<(value: T | PromiseLike<T>) => void>,
reject: (reason?: any) => void) => void
): Promise<T>;
}
}
那更好,对吗?您的function foo(): Promise<string> {
return new Promise((resolve, reject) => { resolve(); }); // error!
// Expected 1 arguments, but got 0 -----> ~~~~~~~~~
}
function bar(): Promise<string | undefined> {
return new Promise((resolve, reject) => { resolve(); }); // okay
}
实现会产生所需的错误,而返回类型为foo()
的类似bar()
实现却不会。
好的,希望能有所帮助;祝你好运!