递归条件类型

Kev*_*eal 3 typescript

我想递归地映射一个对象,以便将对象中的原始值转换为其他类型。

例如,我想要一个这样的对象:

const before = { a: { c: '' }, b: [ '', { d: '' } ] }
Run Code Online (Sandbox Code Playgroud)

变成这样:

const after = { a: { c: Test }, b: [ Test, { d: Test } ] }
Run Code Online (Sandbox Code Playgroud)

我还假设值会不会DateSymbol或空/空。只是 JSON 可序列化类型,如字符串、数字等(空值除外)

这是我尝试过的:

type ConvertToTest<T> = {
    [P in keyof T]: T[P] extends any[]
        ? ConvertToTest<T[P]>
        : T[P] extends {}
            ? ConvertToTest<T[P]>
            : Test;
}

function convert<T>(o: T): ConvertToTest<T> {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这是使用Typescript 2.8 中引入的条件类型

const after = convert(before)结果在编辑器中after.a.c使用string类型补全 for c,而不是补全 for Test

如何重写type ConvertToTest<T>以说服after.a.c类型为 的Typescript Test

编辑:这是说明上述内容的Typescript Playground 链接

CRi*_*ice 5

所以你需要从ConvertToTest<T>类型中获得两件事。一个是 ifT是原始类型,则CovertToTest<T> = Test. 另一个是如果T不是原始的,您希望保留相同的键但转换它们的值。

为此,我只需将第一个 case 添加为条件类型的一部分,然后让另一个分支使用递归映射类型:

type Primitive = string | number | boolean | null | undefined;
type ConvertToTest<T> = T extends Primitive ? Test : {
    [K in keyof T]:
        T[K] extends (infer U)[] ? ConvertToTest<U>[] :
        ConvertToTest<T[K]>;
}
Run Code Online (Sandbox Code Playgroud)

使用它,您可以像这样使用它:

// For example. Replace with whatever your actual type is.
type test = {
    foo(): string;
}

declare function convertToTest<T>(obj: T): ConvertToTest<T>;
const test = convertToTest({ a: "", b: { c: true, primArr: [1, ""], objArr: [{inner: ""}] } });

test.a.foo(); // OK
test.b.c.foo(); // OK
test.b.primArr[0].foo() // OK
test.b.objArr[0].inner.foo() // OK
Run Code Online (Sandbox Code Playgroud)

这是一个很好的方法,因为它适用于任何深度的对象,并且也将正确处理转换数组类型的元素。