如何定义对象类型的真正逻辑或(结果中不混合不同的对象键)[重复]

Posted

技术标签:

【中文标题】如何定义对象类型的真正逻辑或(结果中不混合不同的对象键)[重复]【英文标题】:How to define a true logical OR of object types (no mixing of different object keys in result) [duplicate] 【发布时间】:2021-05-28 16:01:33 【问题描述】:

一个例子:

type TA = a:number,b:number
type TB = a:number,c:number,d:number
const t1:Or<TA,TB> = a:1,b:1 // want
const t2:Or<TA,TB> = a:1,c:1,d:1 // want 
const t3:Or<TA,TB> = a:1,b:1,c:1 // DON'T want 

期望的结果是 t1 有效,因为它完全适合 TAt2 有效,因为它完全适合 TB,但 t3 无效,因为它不完全适合TATB

什么时候

type Or<T,U>=T|U

TypeScript 实际上认为t3 是有效的。 TypeScript union type | 还允许合并键对象,有时还允许合并部分对象。

将每种类型放在一个元素数组中,如下所示:

type T0 = a:number,b:number
type T1 = a:number,c:number,d:number
type Test=[T0]|[T1]
const t1:Test=[a:1,b:1,]  
const t2:Test=[a:1,c:1,d:1]  
const t3:Test=[a:1,b:1,c:1] // fails as desired

有效,但被测对象也必须放在单元素数组中。

有什么办法可以解决吗?

【问题讨论】:

请注意,多余的属性检查仅适用于 object literals,因此这将是 some 实用程序,但仅适用于一些。例如,虽然let x: TA = a:1, b:2, c:3; 会标记额外的c 属性,但let y = a:1, b:2, c:3; let x: TA = y; 不会。 联合 seem to have been played with 和 couple of times 的过多属性检查(请参阅 this search),但未实施。 你可以使用tagged types,但我猜你不喜欢。 这个***.com/questions/65805600/… 的答案可能有用 请不要通过破坏您的帖子为他人增加工作量。通过在 Stack Exchange 网络上发帖,您已在 CC BY-SA 4.0 license 下授予 Stack Exchange 分发该内容的不可撤销的权利(即无论您未来的选择如何)。根据 Stack Exchange 政策,帖子的非破坏版本是分发的版本。因此,任何破坏行为都将被撤销。如果您想了解更多关于删除帖子的信息,请参阅:How does deleting work? 【参考方案1】:

原因

在 TypeScript 中,| 不是运算符*,它表示该类型是左侧和右侧类型的并集。如果您要理解为什么a:number,b:number | a:number,c:number,d:number 允许a:number,b:number,c:number,这一点很重要。

当你声明一个联合时,你告诉编译器一个可分配给联合的类型应该可以分配给它的至少一个成员。考虑到这一点,让我们从这个角度检查a:number,b:number,c:number类型。

联合的左侧成员是a:number,b:number,这意味着可分配给它的类型必须至少有两个number类型的属性:ab(有excess property checks的概念对于对象文字,但是,正如 TJ Crowder 的 mentioned 一样,这不适用于联合)。来自the handbook**:

编译器只检查是否至少存在所需的并匹配所需的类型

因此,由于a:number,b:number,c:number 可分配给a:number,b:number,因此不再需要检查 - 该类型至少满足联合的一项要求。顺便说一句,这种行为完全符合逻辑 OR 的真值表,类似于联合。


您通过将类型包装成元组来解决此问题的尝试依赖于naked vs. wrapped 类型参数行为。因为您将类型包装在元组中,所以编译器会将一个元素的 元组 相互比较。显然,第三个元组与第一个和第二个元组不同,这给了你想要的结果。


什么

您真正想要的是逻辑 XOR 表现出的行为:其中之一,但不是两者。除了使用tagged types(TJ Crowder 的mentioned)之外,还可以定义一个实用程序,将一对类型转换为“来自 A 且存在于两者中但不存在于 A 中的所有道具”类型的联合:

type XOR<A,B> = ( [ P in keyof A ] ?: P extends keyof B ? A[P] : never  & B) | ( [ P in keyof B ] ?: P extends keyof A ? B[P] : never  & A);

这是它的工作原理(该实用程序的权衡是多余的属性被泄露给智能感知,但由于never,立即不允许指定它):

const t0:XOR<TA,TB> = a:1 //property 'b' is missing
const t1:XOR<TA,TB> = a:1,b:1 // OK
const t2:XOR<TA,TB> = a:1,c:1,d:1 // OK 
const t3:XOR<TA,TB> = a:1,b:1,c:1 // types of property 'c' are incompatible

Playground


* | 是运算符的概念出现在 first revision 中,后来被删除

** 必须注意,这并不意味着 all 在找到匹配项时会检查短路。在这种情况下,联合的所有成员本身都是对象文字,因此对已知属性的约束仍然适用于每个成员,如果在赋值期间存在未知属性,则会导致 TS2322 错误:

const t4:XOR<TA,TB> = a:1,b:1,c:1, g:3 //Object literal may only specify known properties

【讨论】:

您的报价:“编译器仅检查是否存在至少所需的那些并匹配所需的类型”。那是为了传递给函数的类型,在这种情况下,任何额外的属性都是允许的。对于文字赋值(在手册同一页的下方),不允许使用额外的属性。联合所允许的既不是这些定义中的一个,也不是另一个,而是一种混合:联合操作数之一必须由属性的子集满足,并且如果它们存在于任何操作数中,则允许单独的附加属性。 (手册中没有描述)。 @CraigHicks - 嗯,是的,你是对的,但我只想强调,由于至少有一个成员匹配,因此不会执行多余的属性检查以确保类型不包含属性 来自其他工会成员。同时,两个成员 都是 对象字面量,因此它们不能自己指定额外的属性(不是因为它们是联合的一部分)。但这并不意味着它是一个特例。回复:引用 - 我会说这是关于接口,而不是函数参数。无论如何,该语句看起来确实没有执行其他检查,因此这部分应该改进。 我同意你所说的一切,这对我来说从一开始就是一种作案手法——坦率地说,我不知道你为什么需要提醒我关于 SO 欺骗系统。也就是说,我也觉得给提问者一个关于他们的情况的彻底解释(不一定指这个 Q)然后指出规范/欺骗可能是有益的 @OlegValter - 好吧,原因不言而喻 - 您在这里而不是那里发布了答案。但无论如何,这些东西是可以解释的,帮助人们总是好的等等。(无论如何我对 SO 的系统有严重的怀疑。:-))快乐编码! @TJCrowder - 如果我一开始就知道他们是欺骗目标,我会在那里发帖(或者可能不会 - 因为事实证明我达到了与其他人相同的技术)回答时:) SO上的问答熵率令人恐惧。看到评论很惊讶 - 谢谢,编码也很愉快!

以上是关于如何定义对象类型的真正逻辑或(结果中不混合不同的对象键)[重复]的主要内容,如果未能解决你的问题,请参考以下文章

c语言 不同数据类型间的混合运算

c++有混合类型吗

R语言数据对象类型

Go 数据类型(三)整型及运算符

Go 数据类型

Java面向对象之多态