【问题标题】:Change type of values in deeply nested object structure更改深度嵌套对象结构中的值类型
【发布时间】:2021-07-14 03:17:58
【问题描述】:

我需要递归地遍历数据结构并创建一个type,其中一些字段根据条件更改为不同的类型。

基于以下结构,我需要创建一个类型(Result),其中所有A 类型都替换为B 类型。

class A{}
class B{}

const data = {
    propA:new AA,
    propB:true,
    nested:{
        propC:new AA,
        propD:false
    }
}

// something like:
type Replace<typeof data, A, B>

// result
type Result = {
    propA:B,
    propB:boolean,
    nested:{
        propC:B
        propD:boolean
    }

}

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    这可以通过条件递归类型来实现:

    class A {
        ClassPropA!: string
    }
    class B {
        ClassPropB!: string
    }
    
    const data = {
        propA: new A(),
        propB: true,
        nested:{
            propC: new A(),
            propD:false
        }
    }
    
    type Replace<T, A, B> = T extends object
        ? { [key in keyof T]: T[key] extends A ? B : Replace<T[key], A, B> }
        : T;
    
    // type Result = {
    //     propA: B,
    //     propB: boolean,
    //     nested:{
    //         propC: B
    //         propD: boolean
    //     }
    //
    // }
    type Result = Replace<typeof data, A, B>;
    

    【讨论】:

    • 这个答案也是正确的,而且更容易推理,但是,我将使用@Oblosys 的答案,因为它包含了额外的检查。所以这次只是我的一个赞成票。
    【解决方案2】:

    您可以使用映射类型执行此操作,但请记住,匹配基于对象结构而不是类名,因此当您定位 A 时,来自 class C{} 的对象也将被转换。

    Replace 类型可以定义为

    type Replace<T, From, To> = T extends (...args: any[]) => any ? T : {
      [K in keyof T]: 
        [T[K], From] extends [From, T[K]] ? To : Replace<T[K], From, To>
    }
    

    第一个条件是保留任何方法/函数属性,因为映射类型会将它们转换为{}。映射类型本身处理每个键,并检查值是否扩展 From 类型和 From 类型扩展值类型,以确保相等。如果两者相等,则将值替换为To,否则递归调用Replace

    这是一个转换示例:

    class A{}
    class B{b = 42}
    class C{}
    
    const data = {
        propA: new A(),
        propBool: true,
        propC: new C(),
        nested:{
            propA: new A(),
            propBool: false
        },
        f: () => 42
    }
    
    type Result = Replace<typeof data, A, B>
    // type Result = {
    //     propA: B;
    //     propBool: boolean;
    //     propC: B;
    //     nested: {
    //         propA: B;
    //         propBool: boolean;
    //     };
    //     f: () => number;
    // }
    

    TypeScript playground

    【讨论】:

    • 为什么要检查From 扩展T[K]
    • @JoeyKilpatrick 为了避免同时替换 From 的子类型。例如,如果From{a: 1},那么如果没有额外的条件,它也会替换{a:1, b:2}
    • 这肯定是您的回答胜过我自己的回答。另外,我从未见过使用元组有效地检查多个extends 条件,非常聪明。给我点赞。
    • 从来不知道你可以检查多个扩展。高级 Typescript 看起来很神奇。
    猜你喜欢
    • 2019-03-15
    • 2021-07-18
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    • 2020-02-15
    • 1970-01-01
    相关资源
    最近更新 更多