【问题标题】:Can I use Typescript mapped types to "flatten" or "unflatten" an object type?我可以使用 Typescript 映射类型来“展平”或“取消展平”对象类型吗?
【发布时间】:2022-01-15 08:55:21
【问题描述】:

假设我们有一个包含一些嵌套属性的类型,如下所示:

type Deep = {
    foo: number;
    bar: {
        nested1: string;
        nested2: number[];
    }
}

但我们需要通过无法处理嵌套属性的系统传递该类型的对象。因此,我们将展平对象,使用_ 之类的分隔符来表示嵌套属性:

type Flat = {
    foo: number;
    bar_nested1: string;
    bar_nested2: number[];
}

我们可以实现扁平化和非扁平化这些对象的函数。但是,如果我们希望函数是通用的,我们如何表示它们的返回类型?。目前(v4.5)甚至可以在 Typescript 中使用吗?

function flatten<TDeep>(deep: TDeep): Flatten<TDeep>; // How do we define `Flatten<>`?
function unflatten<TFlat>(flat: TFlat): Unflatten<TFlat>; // How do we define `Unflatten<>`?

我使用 Typescript 4.5 刺伤了Unflatten&lt;&gt;

type Unflatten<T> = WithoutDeepProps<T> & WithUnflattenedProps<T>

type WithoutDeepProps<T> = {
    [Property in keyof T as Exclude<Property, `${string}_${string}`>]: T[Property];
}

type WithUnflattenedProps<T> = {
    [Property in keyof T as ParentOf<Property>]: {
        [ChildProperty in ChildOf<Property>]: T[Property]
    }
}

type ParentOf<T> = T extends `${infer Parent}_${string}` ? Parent : never;
type ChildOf<T> = T extends `${string}_${infer Child}` ? Child : never;

这种方法有效,但它结合了嵌套的属性类型。所以,使用上面的Flat

// Unflatten<Flat> gives:
foo: number;
bar: {
    nested1: string | number[];
    nested2: string | number[];
}

我什至没有尝试过Flatten&lt;&gt;,因为我不知道如何将一个属性(例如bar)转换为多个属性(bar_nested1bar_nested2

【问题讨论】:

标签: typescript typescript-generics


【解决方案1】:

第一部分是理解为什么你会在那里建立这个联盟。似乎在映射时,如果 as 子句产生重复的属性(就像这里一样,bar_nested1bar_nested1 最终被映射到相同的属性 bar)打字稿将调用属性类型表达式映射类型不是使用每个单独的键,而是使用它们的联合。 (所以Property 将是bar_nested1 | bar_nested1)。因此,当您索引 T[Property] 时,您会得到两种类型的联合。

我们可以使用这种类型来证明这一点:


type WithUnflattenedProps<T> = {
    [Property in keyof T as ParentOf<Property>]: [Property]
}

type Y = Id<WithUnflattenedProps<Flat>>
// Will be 
// type Y = {
//     bar: ["bar_nested1" | "bar_nested2"];
// }

Playground Link

我们可以通过基于ChildProperty 而不是使用Property 重建每个属性来解决此问题:

type WithUnflattenedProps<T> = {
    [Property in keyof T as ParentOf<Property>]: {
      [ChildProperty in ChildOf<Property>]:  T[`${ParentOf<Property>}_${ChildProperty }` & keyof T]
    }
}

Playground Link

您还可以递归地应用该类型以展开更深层的层次结构Playground Link

【讨论】:

    猜你喜欢
    • 2022-12-03
    • 2022-01-22
    • 2016-06-27
    • 2014-02-28
    • 2017-05-14
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    相关资源
    最近更新 更多