如何修改类型的嵌套属性?

Asa*_*viv 6 typescript

我有一个类型

type Foo = {
  a: {
    b: {
      c: string[]
      ...rest
    }
    ...rest
  }
  ...rest
}
Run Code Online (Sandbox Code Playgroud)

我如何更改a.b.c为另一种类型但保留其余属性?

jca*_*alz 9

TypeScript 不提供任何内置的方法来执行此操作,因此如果您想要类似的东西,您必须使用映射条件类型自己构建它。您还需要非常仔细地考虑您想要的行为,因为存在各种潜在的边缘情况,特别是对于可选/只读/等属性和函数/数组。

这是一种方法:

type _Overwrite<T, U> = U extends object ? (
    { [K in keyof T]: K extends keyof U ? _Overwrite<T[K], U[K]> : T[K] } & U
) : U

type ExpandRecursively<T> = T extends Function ? T : T extends object
    ? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never
    : T;

type Overwrite<T, U> = ExpandRecursively<_Overwrite<T, U>>
Run Code Online (Sandbox Code Playgroud)

该类型_Overwrite<T, U>采用一个类型TU并递归地遍历它们的属性,如果存在冲突,则将 from 替换T为 from 。U这种类型应该可以工作,但使用交叉点来表示它,这可能会变得难看。

ExpandRecursively<T>遍历结果类型并将所有属性合并在一起的类型也是如此,所以{a: string} & {b: number}应该成为{a: string, b: number}

并且Overwrite<T, U>只是对结果进行获取_Overwrite<T, U>和使用。ExpandRecursively<>


让我们通过一个例子来看看它的行为方式:

type Foo = {
    a: {
        b: {
            c: string[];
            d: number;
        }
        e: {
            f: boolean;
        }
    };
    g: {
        h?: () => string;
    }
}

type ReplaceFoo = Overwrite<Foo, { a: { b: { c: number } } }>;
Run Code Online (Sandbox Code Playgroud)

这产生:

/*
type ReplaceFoo = {
    a: {
        b: {
            c: number;
            d: number;
        };
        e: {
            f: boolean;
        };
    };
    g: {
        h?: (() => string) | undefined;
    };
}
*/
Run Code Online (Sandbox Code Playgroud)

这对我来说看起来很合理。T但在使用它之前,您需要非常彻底地测试这样的东西:如果和/或是U联合类型,您想做什么?T如果orU是数组或元组,你想做什么?您希望能够区分“替换a.b.cnumber”和“替换a.b{c: number}”吗?(也就是说,您是否想要选择“删除”而不是“替换”子属性?)这些问题(可能还有其他问题)的所有答案都将对您想要如何编写Overwrite<T, U>.

希望这能给您一些如何继续前进的想法。祝你好运!

Playground 代码链接