简体   繁体   English

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

[英]Why does TypeScript assertion of object literal `{a}` work with interface `{a, b}` but not `{a?, b}`

Why does the following assertion work: 为什么以下断言有效:

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

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

But this assertion gives an error: 但是这个断言给出了一个错误:

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

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

The only difference I can see is making one of the interface properties optional ( ? ). 我能看到的唯一区别是使其中一个接口属性可选( ? )。 It seems that if all properties are not optional, I can assert a partial object to the interface, but as soon as any of the interface properties are optional, I cannot assert a partial object anymore. 似乎所有属性都不是可选的,我可以断言接口的部分对象,但只要任何接口属性是可选的,我就不能断言部分对象了。 This doesn't really make sense to me and I've been unable to find an explanation of this behavior. 这对我来说没有意义,我一直无法找到这种行为的解释。 What's going on here? 这里发生了什么?


For context: I encountered this behavior while trying to work around the problem that React's setState() takes a partial state object, but TypeScript doesn't yet have partial types to make this work properly with your state interface. 对于上下文:我在尝试解决React的setState()采用部分状态对象的问题时遇到了这种行为 ,但是TypeScript还没有部分类型来使这个状态接口正常工作。 As a workaround I came up with setState({a: "a"} as MyState) and found this works as long as interface MyState fields are all non-optional, but fails as soon as some properties are optional. 作为一种变通方法,我想出了setState({a: "a"} as MyState)发现只要这个工程作为接口MyState领域都是非可选,但只要一些属性是可选的失败。 (Making all properties optional is a workaround, but very undesirable in my case. ) (使所有属性都是可选的是一种解决方法,但在我的情况下非常不受欢迎。)

Type assertions can only be used to convert between a type and a subtype of it. 类型断言只能用于在类型和子类型之间进行转换。

Let's say you declared the following variables: 假设您声明了以下变量:

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

Note number is a subtype of number | string 注释numbernumber | string子类型 number | string , meaning any value that matches the type number (eg 3 ) also matches number | string number | string ,表示与类型number匹配的任何值(例如3 )也匹配number | string number | string . number | string Therefore, it is allowed to use type assertions to convert between these types: 因此,允许使用类型断言在这些类型之间进行转换:

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

Similarly, { a: string, b: string } is a subtype of { a: string } . 同样, { a: string, b: string }{ a: string }的子类型。 Any value that matches { a: string, b: string } (eg { a: "A", b: "B" } ) also matches { a: string } , because it has an a property of type string . 匹配{ a: string, b: string }任何值(例如{ a: "A", b: "B" } )也匹配{ a: string } ,因为它具有string类型a属性。

In contrast, neither of { a?: string, b: string } or { a: string } is a subtype of the other. 相反, { a?: string, b: string }{ a: string }都不是另一个的子类型。 Some values (eg { b: "B" } ) match only the former, and others (eg { a: "A" } ) match only the latter. 一些值(例如{ b: "B" } )仅匹配前者,而其他值(例如{ a: "A" } )仅匹配后者。

If you really need to convert between unrelated types, you can work around this by using a common supertype (such as any ) as an intermediate: 如果您确实需要在不相关的类型之间进行转换,可以使用常用的超类型(例如any )作为中间函数来解决此问题:

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

The relevant part in the spec is 4.16 Type Assertions : 规范中的相关部分是4.16类型断言

In a type assertion expression of the form < T > e, e is contextually typed (section 4.23) by T and the resulting type of e is required to be assignable to T, or T is required to be assignable to the widened form of the resulting type of e, or otherwise a compile-time error occurs. 在形式<T> e的类型断言表达式中,e由T进行上下文类型化(第4.23节),并且所得到的e类型需要可分配给T,或者T需要可分配给扩展形式的产生的e类型,否则发生编译时错误。

Well, this is only a theory but I have some code to back it up. 嗯,这只是一个理论,但我有一些代码支持它。

In your first example, when casting to AllRequired you're basically saying to the compiler the you know what you're doing and you'll set the value for all.b later on. 在你的第一个例子中,当转换为AllRequired你基本上是对编译器说你知道你在做什么,然后你将为all.b设置值。

In the second example you think you're doing the same but the compiler probably thinks "hey, if he sets the optional property he must be setting a full object" and so it's confused as for why the value for some.b is missing from this full object. 在第二个例子中,你认为你做的是相同的,但是编译器可能认为“嘿,如果他设置了可选属性,他必须设置一个完整的对象”,所以它很混淆为什么some.b的值丢失了这个完整的对象。

This may sound a bit far fetched, but here: 这可能听起来有点牵强,但在这里:

let some2 = (all.a ? {a: "foo"} : {}) as SomeOptional

The error is gone if you use a condition because then (well, probably) some2.a is really optional. 如果您使用条件,则错误消失,因为那时(好吧,可能) some2.a是可选的。

Try it in playground . 操场上试试吧。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM