为什么TypeScript声明对象文字`{a}`使用接口`{a,b}`而不是`{a?,b}`

Aar*_*all 7 typescript reactjs

为什么以下断言有效:

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

let all = {a: "foo"} as AllRequired; // No error
Run Code Online (Sandbox Code Playgroud)

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

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

let some = {a: "foo"} as SomeOptional; // Error: Property 'b' missing
Run Code Online (Sandbox Code Playgroud)

我能看到的唯一区别是使其中一个接口属性为optional(?).似乎所有属性都不是可选的,我可以断言接口的部分对象,但只要任何接口属性是可选的,我就不能断言部分对象了.这对我来说没有意义,我一直无法找到这种行为的解释.这里发生了什么?


对于上下文:我在尝试解决React 采用部分状态对象的问题时遇到了这种行为setState(),但是TypeScript还没有部分类型来使这个状态接口正常工作.作为一种变通方法,我想出了setState({a: "a"} as MyState),发现这个工程只要接口MyState字段是所有非可选,但一旦失败,一些属性是可选的.(使所有属性都是可选的是一种解决方法,但在我的情况下非常不受欢迎.)

Spi*_*ike 2

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

假设您声明了以下变量:

declare var foo: number | string;
declare var bar: number;
Run Code Online (Sandbox Code Playgroud)

Notenumber是 的子类型number | string,意味着与该类型number(例如3)匹配的任何值也匹配number | string。因此,允许使用类型断言在这些类型之间进行转换:

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

同样,{ a: string, b: string }是 的子类型{ a: string }。任何匹配的值{ a: string, b: string }(例如{ a: "A", b: "B" })也匹配{ a: string },因为它具有atype 属性string

相反,两者都不{ a?: string, b: string }{ a: string }另一个的子类型。某些值(例如{ b: "B" })仅匹配前者,而其他值(例如{ a: "A" })仅匹配后者。

如果您确实需要在不相关的类型之间进行转换,则可以通过使用公共超类型(例如any)作为中间体来解决此问题:

let foo = ({ a: "A" } as any) as { a?: string, b: string };
Run Code Online (Sandbox Code Playgroud)

规范中的相关部分是4.16 Type Assertions

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