为什么 TypeScript 对对象字面量 `{a}` 的断言适用于接口 `{a, b}` 而不是 `{a?, b}` [英] Why does TypeScript assertion of object literal `{a}` work with interface `{a, b}` but not `{a?, b}`

查看:44
本文介绍了为什么 TypeScript 对对象字面量 `{a}` 的断言适用于接口 `{a, b}` 而不是 `{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;

注意numbernumber | 的子类型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屋!

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