为什么 TypeScript 对对象字面量 `{a}` 的断言适用于接口 `{a, b}` 而不是 `{a?, b}` [英] Why does TypeScript assertion of object literal `{a}` work with interface `{a, b}` but not `{a?, b}`
问题描述
为什么以下断言有效:
interface AllRequired {a:字符串;b:字符串;}let all = {a: "foo"} as AllRequired;//没有错误
但是这个断言给出了一个错误:
interface SomeOptional {a?:字符串;b:字符串;}让 some = {a: "foo"} 作为 SomeOptional;//错误:缺少属性b"
我能看到的唯一区别是将接口属性之一设为可选(?
).似乎如果所有属性都不是可选的,我可以对接口断言部分对象,但是只要任何接口属性是可选的,我就不能再断言部分对象.这对我来说没有意义,我一直无法找到这种行为的解释.这是怎么回事?
对于上下文:我在尝试解决以下问题时遇到此行为React 的 setState()
接受部分状态对象,但 TypeScript 没有还具有部分类型 以使其与您的状态接口正常工作.作为一种解决方法,我想出了 setState({a: "a"} as MyState)
并发现只要接口 MyState
字段 all 就可以工作em> 非可选,但只要 some 属性是可选的,就会失败.(使所有属性可选是一种解决方法,但在我的情况下非常不受欢迎.)
类型断言只能用于类型和子类型之间的转换.
假设您声明了以下变量:
declare var foo: number |细绳;声明 var bar: number;
注意number
是number | 的子类型string
,意味着任何匹配类型 number
(例如 3
)的值也匹配 number |字符串
.因此,允许使用类型断言在这些类型之间进行转换:
bar = foo 作为数字;/* 转换为子类型 */foo = bar 作为数字 |细绳;/* 转换为超类型(断言不是必需的但允许)*/
同样,{ a: string, b: string }
是 { a: string }
的子类型.任何匹配 { a: string, b: string }
的值(例如 { a: "A", b: "B" }
)也匹配 { a: string }
,因为它有一个 a
类型的 string
属性.
相比之下,{ a?: string, b: string }
或 { a: string }
都不是另一个的子类型.某些值(例如 { b: "B" }
)仅匹配前者,而其他值(例如 { a: "A" }
)仅匹配后者.>
如果您真的需要在不相关的类型之间进行转换,您可以通过使用公共超类型(例如 any
)作为中间体来解决这个问题:
let foo = ({ a: "A" } as any) as { a?: string, b: string };
<小时>
规范中的相关部分是4.16 Type Assertion一个>:
<块引用>在
形式的类型断言表达式中T > e, e 由 T 上下文类型化(第 4.23 节)并且 e 的结果类型必须可分配给 T,或者 T 必须可分配给结果类型的加宽形式的 e,否则会发生编译时错误.
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. 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. (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
, meaning any value that matches the type number
(e.g. 3
) also matches 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 }
. Any value that matches { a: string, b: string }
(e.g. { a: "A", b: "B" }
) also matches { a: string }
, because it has an a
property of type string
.
In contrast, neither of { a?: string, b: string }
or { a: string }
is a subtype of the other.
Some values (e.g. { b: "B" }
) match only the former, and others (e.g. { a: "A" }
) match only the latter.
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:
let foo = ({ a: "A" } as any) as { a?: string, b: string };
The relevant part in the spec is 4.16 Type Assertions:
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.
这篇关于为什么 TypeScript 对对象字面量 `{a}` 的断言适用于接口 `{a, b}` 而不是 `{a?, b}`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!