代码之家  ›  专栏  ›  技术社区  ›  Asaf Aviv

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

  •  1
  • Asaf Aviv  · 技术社区  · 5 年前

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

    我怎样才能改变 a.b.c

    0 回复  |  直到 5 年前
        1
  •  1
  •   jcalz    5 年前

    TypeScript不提供任何内建的方法来实现这一点,所以如果你想要这样的东西,你必须自己来构建它 mapped conditional 类型。您还需要非常仔细地考虑您想要的行为,因为存在各种潜在的边缘情况,特别是对于可选的/readonly/etc属性和函数/数组。

    有一种方法:

    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> 接受类型 T U T型 有来自 如果有冲突。这种类型应该可以工作,但是使用 intersections 代表它,可能会变得丑陋。

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

    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型 和/或 union types ? 你想做什么如果 U型 是数组还是数组 tuple a.b.c 具有 number a.b 具有 {c: number} "? (也就是说,是否要选择“擦除”而不是“替换”子属性?)所有这些问题的答案(可能还有其他问题)都会对你的写作有一定的影响 覆盖<T,U> .

    希望这能给你一些前进的思路。祝你好运!

    Playground Link to code