首页 > 解决方案 > 对象重映射器的高级类型

问题描述

我正在尝试创建一个函数,让调用者重新映射给定特定映射的对象。结果对象应该能够知道新的新字段名称和类型。

这在打字稿中可能吗?我认为此时我需要的只是返回类型。这是我拥有的非工作代码:

const mapping = {
    a: "learn",
    b: "state"
}

const original = {
    a: false,
    b: 0
}

const map = <
    Mapping,
    Original
>(
    mapping: Mapping,
    states: Original
): {
    [key: Mapping[key]]: Original[key] //WHAT IS THE CORRECT TYPE HERE?
} => {
    return Object.keys(original).reduce((total, key) => {
        return {
            ...total,
            [mapping[key]]: original[key] //THERE IS AN ERROR HERE TOO BUT I AM NOT WORRIED ABOUT THAT RIGHT NOW
        }
    }, {})
}

const remapped = map(mapping, original)

console.log(remapped)
console.log(remapped.learn)
console.log(remapped.state)

我本质上是在尝试重命名alearn和。代码正常工作,但我收到类型错误('key' 指的是一个值,但在这里被用作一种类型。)。任何帮助将不胜感激!bstate

标签: typescript

解决方案


首先,您的工具箱中需要@jcalz 令人惊叹的UnionToIntersection实用程序类型。

type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never;

现在让我们创建自己的实用程序类型:

1    type Remap<A extends { [k: string]: string }, B> =
2        keyof B extends keyof A ?
3        { [P in keyof B & keyof A]: { [k in A[P]]: B[P] } } extends
4        { [s: string]: infer V } ? 
5        UnionToIntersection<V>
6        : never
7        : never;

解释:

  • ln:1,将 A 的值约束为字符串。
  • ln:2,约束所有 B 的键都存在于 A
  • ln:3,映射到{ "a": {"learn": boolean}, "b": {"state": number} }
  • ln:4,取值部分,变为:{"learn": boolean} | {"state": number}union
  • ln:5,UnionToIntersection施展魔法!

把东西放在一起:

const mapping = {
    a: "learn",
    b: "state",
} as const;   // mark as `const` is necessary to infer value as string literal
              // else it'll just be string
const original = {
    a: false,
    b: 0,
};

const map = <
    M extends { [k: string]: string },
    O extends { [k: string]: any }
>(mapping: M, original: O): Remap<M, O> => {
    return Object.keys(original).reduce((total, key) => {
        return {
            ...total,
            [mapping[key]]: original[key]
        }
    }, {} as any); // as any to mute error
};

操场


我遇到了 jcalz 的另一个答案,它提供了比我更优雅的解决方案。检查该答案以获得解释。我将在此处附加它:

type Remap2<
    M extends { [k: string]: string },
    O extends { [P in keyof M]: any }
> = {
    [P in M[keyof M]]: O[
        { [K in keyof M]: M[K] extends P ? K : never }[keyof M]
    ]
};

推荐阅读