为什么没有严格检查 promise 回调结果类型? [英] Why are promise callback result types not strictly checked?

查看:109
本文介绍了为什么没有严格检查 promise 回调结果类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Typescript 中,我有一个接口 MyInterface 定义如下:

In Typescript, I have an interface MyInterface defined as below:

interface MyInterface {
    hello: string
}

使用它来定义对象不允许我包含接口未描述的属性.以下会产生一个错误,指示属性 what 是不允许的,因为它没有在 MyInterface 中定义:

Using it to define an object does not allow me to include properties which are not described by the interface. The following produces an error indicating that the property what is not allowed because it is not defined in MyInterface:

const testObject: MyInterface = {
    hello: 'world',
    what: 'is going on',
    ^^^^^^^^^^^^^^^^^^^
}

然而,使用它作为承诺结果类型允许我返回未在 MyInterface 中定义的属性;以下不会产生错误:

However, using it as a promise result type allows me to return properties that are not defined in MyInterface; the following does not produce an error:

const testPromise: Promise<MyInterface> = Promise.resolve({
    hello: 'world',
    what: 'is going on',
});

期望在第二个代码段中出现相同的错误是否合理?如果没有,有没有办法像对象赋值示例中那样使 promise 的返回类型严格?

Is it reasonable to expect the same error to occur in the second snippet? If not, is there a way to make the return type of the promise strict as in the object assignment example?

推荐答案

您所说的严格"通常被称为确切类型".精确的对象类型只接受定义中提到的特定属性,而不精确或开放"的类型将接受额外的属性.在 TypeScript 中,类型通常被视为开放的而不是精确的.这对于子类型化和扩展接口非常有用:

What you're calling "strict" is conventionally referred to as "exact types". Exact object types would only accept the specific properties mentioned in their definitions, whereas inexact or "open" types will accept extra properties. In TypeScript, types are generally treated as open and not exact. This is very good for subtyping and extending interfaces:

interface Foo {
  a: string
}

interface Bar extends Foo {
  b: number
}

const bar: Bar = {a: "hey", b: 123};
const foo: Foo = bar; // okay

如果 Foo 是精确的,那么 Bar 将不是 Foo 的有效扩展,并且 bar 会不是 Foo 的有效实例,并且语言的几乎整个接口/类子类型/扩展/继承功能都将被破坏.

If Foo were exact, then Bar would not be a valid extension of Foo, and bar would not be a valid instance of Foo, and just about the whole interface/class subtyping/extension/inheritance features of the language would be broken.

但发现这种开放类型并没有捕捉到一类常见的错误,即人们会拼错可选属性的键.如果属性是可选的,那么可以将其省略,如果对象类型是开放的,那么拼写错误的键只是一些额外的属性.为了解决这个问题,该语言还会多余的属性检查 对于这种情况,在非常有限的情况下,某些类型被视为是精确的.

But it was discovered that this kind of open type didn't catch a common class of bugs, where people would misspell an optional property's key. If the property is optional, then it's okay to leave it out, and if object types are open, then the misspelled key is just some extra property. To deal with this, the language also does excess property checking for which, in very limited circumstances, some types are treated as if they were exact.

具体来说,只有当您尝试在需要具体对象类型的地方使用全新(新鲜")对象字面量时,才会发生这种情况.因此,如您所见,以下内容将是错误的:

Specifically, this only happens when you are trying to use a brand new ("fresh") object literal in a place that expects a concrete object type. So the following, as you saw, would be an error:

const excessError: Foo = { a: "hey", b: 123 }; // error!
//  Object literal may only specify known properties, and 'b' does not exist in type 'Foo'.

如果在没有预期类型的​​地方使用对象字面量,它将被接受:

If you use an object literal in a place without an expected type, it will be accepted:

const noTypeExpected = { a: "hey", b: 123 }; // no error

如果您重用不再新鲜"的现有对象文字类型,它将被接受:

And if you reuse an existing object literal type that isn't "fresh" anymore, it will be accepted:

const notBrandNew: Foo = noTypeExpected; // no error

如果您使用通过泛型函数传递对象字面量,则返回的类型将不再被视为新鲜"类型:

And if you use pass an object literal through a generic function, the returned type will not be considered a "fresh" type anymore:

const identity = <T>(x: T) => x;
const alsoNotBrandNew: Foo = identity({ a: "hey", b: 123 }); // no error

<小时>

那么现在让我们看看承诺问题:


So now let's look at the promise issue:

const promiseFoo: Promise<Foo> = Promise.resolve({ a: "hey", b: 123 }); // okay

这是可行的,因为 Promise.resolve(value) 是一个带有 以下签名:

This works because Promise.resolve(value) is a generic function with the following signature:

interface PromiseConstructor {
  resolve<T>(value: T | PromiseLike<T>): Promise<T>;
}

类型参数T是从value的类型推断出来的,即{a: string, b: number}.所以返回类型是Promise<{a: string, b: number}>.这可以分配给 Promise<{a: string}>(因为 Promisecovariant 在其类型参数中,除非出现提示,否则我不会在这里介绍)因为 {a: string, b: number}type 不再被视为新的对象字面量类型.

The type parameter T is inferred from the type of value, which is {a: string, b: number}. So the return type is Promise<{a: string, b: number}>. And that is assignable to Promise<{a: string}> (since Promise is covariant in its type argument, which I won't get into here unless prompted) because that {a: string, b: number} type isn't considered a fresh object literal type anymore.

那么,我们如何从 Promise.resolve() 中获得精确"的行为?我能想到的最简单的方法是手动将泛型类型参数指定为 Foo 而不是从 value 的类型推断它:

So, how can we get "exact" behavior out of Promise.resolve()? The easiest way I can think of is to specify the generic type parameter manually as Foo instead of having it be inferred from the type of value:

const promiseExact = Promise.resolve<Foo>({ a: "hey", b: 123 }); // error!
// Object literal may only specify known properties, 
// and 'b' does not exist in type 'Foo | PromiseLike<Foo>'.

现在发生错误是因为 Promise.resolve(value) 期望 value 是一个 Foo |PromiseLike<Foo>,并且新的对象文字被仔细检查,就好像它是一个精确的类型一样.

Now the error occurs because Promise.resolve<Foo>(value) expects value to be a Foo | PromiseLike<Foo>, and the fresh object literal is scrutinized as if it were of an exact type.

好的,希望有帮助;祝你好运!

Okay, hope that helps; good luck!

链接到代码

这篇关于为什么没有严格检查 promise 回调结果类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆