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

时间:2020-01-01 18:14:34

标签: typescript

我有类型

type Foo = {
  a: {
    b: {
      c: string[]
      ...rest
    }
    ...rest
  }
  ...rest
}

如何将a.b.c更改为其他类型,但保留其余属性?

1 个答案:

答案 0 :(得分:1)

TypeScript不提供任何内置方法来执行此操作,因此,如果您想要类似的内容,则必须使用mappedconditional类型自己构建。您还需要非常小心地仔细考虑所需的行为,因为存在各种潜在的极端情况,尤其是对于可选/只读/等属性和函数/数组。

这是一种实现方法:

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>>

类型_Overwrite<T, U>接受类型TU并递归地遍历它们的属性,如果存在,将T中的那些替换为U中的那些是一个冲突。这种类型应该可以使用,但是使用intersections来表示它,这可能很难看。

因此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 } } }>;

这将产生:

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

对我来说哪个看起来合理。但是,您需要在使用前进行彻底的测试:如果T和/或Uunion types,您想做什么?如果TU是数组或tuple,该怎么办?您是否希望能够将“ a.b.c替换为number”和“ a.b替换为{c: number}”之间进行区分? (也就是说,您是否希望选择“擦除”而不是“替换”子属性?)所有这些问题(可能还有其他问题)的答案都会与您如何编写{{ 1}}。

希望可以为您提供一些前进的思路。祝你好运!

Playground Link to code

相关问题