更改深度嵌套对象结构中的值类型

时间:2021-04-19 22:46:24

标签: typescript typescript-generics

我需要递归地遍历数据结构并创建一个 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
    }

}

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分: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>;