为什么对象文字“{a}”的 TypeScript 断言适用于接口“{a, b}”,但不适用于“{a?, b}”

2023-11-24

为什么以下断言有效:

interface AllRequired {
    a: string;
    b: string;
}

let all = {a: "foo"} as AllRequired; // No error

但这个断言给出了一个错误:

interface SomeOptional {
    a?: string;
    b: string;
}

let some = {a: "foo"} as SomeOptional; // Error: Property 'b' missing

我能看到的唯一区别是使接口属性之一成为可选(?)。似乎如果所有属性都不是可选的,我可以向接口断言部分对象,但是一旦任何接口属性都是可选的,我就无法再断言部分对象。这对我来说确实没有意义,而且我一直无法找到这种行为的解释。这里发生了什么?


对于上下文:我遇到这种行为在尝试解决 React 的问题时setState()接受部分状态对象,但是TypeScript 尚无部分类型使其与您的状态接口正常工作。作为一种解决方法,我想出了setState({a: "a"} as MyState)并发现只要界面就可以工作MyState字段是all不可选,但一旦失败就会失败some属性是可选的。 (将所有属性设置为可选是一种解决方法,但在我的情况下非常不可取。)


类型断言只能用于在类型与其子类型之间进行转换。

假设您声明了以下变量:

declare var foo: number | string;
declare var bar: number;

Note number is a subtype of number | string,表示与类型匹配的任何值number (e.g. 3) 也匹配number | string。因此,允许使用类型断言在这些类型之间进行转换:

bar = foo as number; /* convert to subtype */
foo = bar as number | string; /* convert to supertype (assertion not necessary but allowed) */

相似地,{ a: string, b: string }是一个子类型{ a: string }。任何匹配的值{ a: string, b: string } (e.g. { a: "A", b: "B" }) 也匹配{ a: string },因为它有一个a类型的属性string.

相比之下,两者都没有{ a?: string, b: string } or { a: string }是另一个的子类型。 一些值(例如{ b: "B" })仅匹配前者,其他(例如{ a: "A" }) 仅匹配后者。

If you really需要在不相关的类型之间进行转换,您可以通过使用通用的超类型(例如any)作为中间体:

let foo = ({ a: "A" } as any) as { a?: string, b: string };

规范中的相关部分是4.16 类型断言:

在 e 形式的类型断言表达式中,e 由 T 进行上下文类型化(第 4.23 节),并且结果类型为e需要可分配给 T,或者 T 需要可分配给 e 的结果类型的扩展形式,否则会发生编译时错误。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么对象文字“{a}”的 TypeScript 断言适用于接口“{a, b}”,但不适用于“{a?, b}” 的相关文章

随机推荐