我有过

type a = { a: "hi", b: number }
type b = { a: "bye", b: number }

我想以某种方式go 做:

type c = a + b // there's nothing called "+"

并得到c分,推断为

type c = { a: "hi" | "bye", b: number };

我试着go 做:

import { UnionToIntersection } from 'utility-types'

type a = { a: "hi", b: number }
type b = { a: "bye", b: number }
type c = UnionToIntersection<a | b> // never

c被推断为never.


==同步,由长者更正==

如果您不介意的话,这里有另一个用例

type o = {
  type: "bar";
  foo: string;
  bar: number;
} | {
  type: "baz";
  foo: string;
  baz: boolean;
}

我想以某种方式将此类型转换为以下类型:

type: "bar" | "baz"  ;
foo: string;
bar?: number;
baz?: boolean;

推荐答案

您请求的操作非常接近于identity操作;如果您有一个union type,就像A | B一样,并访问其属性,则每个属性都将自动成为并集.如果这就是您想要的全部,那么您可以按原样使用联合,或者如果您想要的话,编写一个mapped type来将联合压缩为单一的对象类型.

这里唯一的问题是,您希望只出现在某些联盟成员中的属性在组合类型中为optional,而联盟往往会忽略这些属性.因此,我们应该做的第一件事是将联合的每个成员转换为一个新类型,对于未在该成员中定义的任何属性,具有类型never的可选属性.也就是说,我们想把{a: 0, b: 1} | {b: 2, c: 3}变成类似{a: 0, b: 1, c?: never} | {a?: never, b: 2, c: 3}的东西.然后,当合并时,它们将按照需要合并成{a?: 0, b: 1 | 2, c?: 3}.

把这些放在一起看起来是这样的:

type _Combine<T, K extends PropertyKey = T extends unknown ? keyof T : never> =
    T extends unknown ? T & Partial<Record<Exclude<K, keyof T>, never>> : never;

type Combine<T> = { [K in keyof _Combine<T>]: _Combine<T>[K] }

这里的_Combine<T>是一个帮助器类型,它使用distributive conditional typesT拆分成联合成员并对它们执行操作.第一个是Kdefault type argument,它收集T(T extends unknown ? keyof T : never)成员的所有键,第二个是intersect,其中T的每个成员都有一个对象,对于K中不属于T的成员的每个键,都有一个never类型的可选键.

然后Combine<T>_Combine<T>之上的身份映射类型,它将({a: 0, b: 1) & Partial<Record<"c", never>>) | ({b: 2, c: 3} & Partial<Record<"a", never>>)等难看的东西折叠为所需的{a?: 0, b: 1 | 2, c?: 3}.


让我们在你的例子中测试一下:

type O = {
    type: "bar";
    foo: string;
    bar: number;
} | {
    type: "baz";
    foo: string;
    baz: boolean;
}

type Z = Combine<O>;
/* type Z = {
    type: "bar" | "baz";
    foo: string;
    bar?: number | undefined;
    baz?: boolean | undefined;
} */

看上go 不错.typefoo属性是必需的,因为它们存在于O的所有成员中,而barbaz属性是可选的,因为它们至少在O的一个成员中缺失.

Playground link to code

Typescript相关问答推荐

如何从TypScript中的接口中正确获取特定键类型的所有属性?

TypeScript类型不匹配:在返回类型中处理已经声明的Promise Wrapper

如何设置绝对路径和相对路径的类型?

如何使我的函数专门针对联合标记?

如何消除在Next.js中使用Reaction上下文的延迟?

用于验证字符串变量的接口成员

CDK从可选的Lambda角色属性承担角色/复合主体中没有非空断言

为什么&;(交集)运算符会删除不相交的属性?

按函数名的类型安全类型脚本方法包装帮助器

基元类型按引用传递的解决方法

@TANSTACK/REACT-QUERY中发生Mutations 后的数据刷新问题

判断映射类型内的键值的条件类型

从route.参数获取值.订阅提供";无法读取未定义";的属性

打字脚本中的模块化或命名空间

基于泛型的类型脚本修改类型

为什么`map()`返回类型不能维护与输入相同数量的值?

在Nestjs中,向模块提供抽象类无法正常工作

React router - 如何处理嵌套路由?

在Typescript 中,是否有一种方法来定义一个类型,其中该类型是字符串子集的所有可能组合?

如果父级被删除或删除,如何自动删除子级?