打字稿展开对象类型

时间:2021-06-08 06:51:56

标签: typescript

type A = {
  a: {
    b: string
    c: string
  }
  x: {
    y: number
    z: number
  }
}

我想:

type B = {
  b: string
  c: string
  y: number
  z: number
}

... 通过泛型实现

type B = Unfold<A>

如何做到这一点?

2 个答案:

答案 0 :(得分:2)

首先,我们需要获取 A(或任何类似 A 的通用对象)的每个键的值:

type GetValues<T extends {[key: string]: any}> = T[keyof T];

这个泛型让我们传入一个类似 A 的对象,并在 A 的每个键处生成所有对象的 union。出于演示目的,让我们使用它来声明一个基于 Foo 的新类型 A

type Foo = GetValues<A>;

如果我们将鼠标悬停在 Foo 上,我们将看到它代表的类型 - 如上所述,A 的每个键处的所有对象的并集。

enter image description here

这很接近,但我们不想要联合,我们想要所有可能类型的 intersection,根据您的要求。幸运的是,SO 上的 this post 已经解决了将联合转换为交集的问题。

type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never;

现在我们拥有了我们需要的一切,我们可以构建我们最终的泛型 Unfold<T> 来组成这两个位:

type Unfold<T extends {[key: string]: any}> = UnionToIntersection<GetValues<T>>;

现在我们可以使用 Unfold<T> 将任何类似 A 的类型定义转换为其“展开”版本:

type UnfoldedA = Unfold<A>;

const foobar: UnfoldedA = {b: '', c: '', y: 0, z: 0};

All put together

答案 1 :(得分:2)

我希望 smbd 会想出一些更具可读性的东西 ))

type A = {
    a: {
        b: string
        c: string
    }
    x: {
        y: number
        z: number,
        w: {
            u: number
        }
    }
}
type Primitives = string | number | boolean | symbol

/**
 * Get all valid nested pathes of object
 */
type AllProps<Obj, Cache extends Array<Primitives> = []> =
    Obj extends Primitives ? Cache : {
        [Prop in keyof Obj]:
        | [...Cache, Prop] // <------ it should be unionized with recursion call
        | AllProps<Obj[Prop], [...Cache, Prop]>
    }[keyof Obj]

// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
    k: infer I
) => void
    ? I
    : never;

/**
 * Iterate through each array of nested keys
 * and get last Prop/Value
 */
type Util<Obj, Props extends ReadonlyArray<Primitives>> =
    Props extends []
    ? Obj
    : Props extends [infer First]
    ? First extends keyof Obj
    ? Obj[First] extends Primitives ? Record<First, Obj[First]> : {}
    : never
    : Props extends [infer Fst, ...infer Tail]
    ? Fst extends keyof Obj
    ? Tail extends string[]
    ? Util<Obj[Fst], Tail>
    : never
    : never
    : never

type Unfold<T> = UnionToIntersection<Util<A, AllProps<T>>>

type Result = Unfold<A>

Playground

关于 AllPropsUtil 实用程序的说明,您可以找到 in my blog