首页 > 解决方案 > 如何将承诺响应映射到节点打字稿中的对象?

问题描述

我有来自 3rd 方图书馆的回复

{
    "public_id": "123456",
    "Name": "John"
}

我需要映射到

{
    "Id": "123456",
    "FirstName": "John"
}

我需要有两个模型来做到这一点吗?还是我有一个注释可以自动映射节点打字稿中的属性?请告诉我。谢谢

标签: node.jstypescript

解决方案


这不会自动发生。如果要将对象的键映射到不同的键集,则需要在运行时以某种形式完成该工作。一种方法是创建一个映射键的辅助函数,将其放入库中,并根据需要使用它。


这是一种可能的实现,假设您使用的是 TypeScript 4.1 并且拥有 ES2019+ 运行时:

const keyMapper =
    <M extends Record<keyof M, K>, K extends PropertyKey>(map: M) =>
        <T extends object>(obj: T): { [K in keyof T as K extends keyof M ? M[K] : K]: T[K] } =>
            Object.fromEntries(Object.entries(obj).
                map(([k, v]) => [k in map ? map[k as keyof M] : k, v])
            ) as any;

在我试图解释之前,让我们看看它的实际效果。我们可以使用keyMapper()创建一个新函数,专门用于将您的 api 响应与public_idName键转换为您所需的值类型,并使用IdFirstName键:

const mapApiVal = keyMapper({ public_id: "Id", Name: "FirstName" });

mapApiVal函数也是如此。如果我们有这样的 api 响应:

const apiVal = {
    "public_id": "123456",
    "Name": "John"
}

我们可以将其转换为所需的形式:

const mapped = mapApiVal(apiVal);
/* const mapped: {
    Id: string;
    FirstName: string;
} */
console.log(mapped); // { "Id": "123456", "FirstName": "John" } 
console.log(mapped.Id.repeat(2)); //123456123456
console.log(mapped.FirstName.toUpperCase()); // JOHN

所以它有效,耶!


那么它是怎样工作的?它有两个部分:打字实现。打字只是调用签名:

/* const keyMapper: <M extends Record<keyof M, K>, K extends PropertyKey>(
    map: M) => <T extends object>(obj: T) => 
    { [K in keyof T as K extends keyof M ? M[K] : K]: T[K]; } */

它说这keyMapper是一个通用函数,它接受map一个对象类型的参数,M其键是任何东西,并且其属性本身是类键(stringnumbersymbol)......它返回一个函数。它返回的函数也是通用的,接受obj一个对象类型的参数T,并返回一个值。它返回的值是具有重新映射键的映射类型(如 TypeScript 4.1 中所介绍的)。它说:对于's typeK中的每个键,我们检查它是否是 的键之一。如果是这样,我们替换为(at key的值)。如果没有,我们离开objTMKM[K]MKK独自的。T[K]我们独自离开这处房产。

现在进行实施。它看起来像这样(删除了类型信息):

        map => obj => Object.fromEntries(Object.entries(obj).
            map(([k, v]) => [k in map ? map[k] : k, v])
        );

在这里,我们使用Object.entries()to 转换obj为键值对数组,处理这些对,然后使用Object.fromEntries()to 将其转回对象。处理与上面的键重映射键入非常相似:对于每个键k,我们检查它是否是 的键map。如果是,我们将其替换为map[k],否则我们将其保留为k。并且价值v被单独留下。

不幸的是,编译器不够复杂,无法验证实现是否满足类型。为了防止编译器警告并让编译器接受实现,我不得不使用一些类型断言:as keyof Mas any. 这意味着我有责任为自己实现类型安全,无论好坏。


Playground 代码链接


推荐阅读