如何将 DynamoDB AttributeMap 类型映射到接口?

问题描述 投票:0回答:7

假设我有一个打字稿界面:

interface IPerson {
    id: string,
    name: string
}

我在 dynamo 中的人员表上运行表扫描,我想要做的是:

const client = new AWS.DynamoDB.DocumentClient();

client.scan(params, (error, result) => {
    const people: IPerson[] = result.Items as IPerson[];
};

我收到错误

Type 'AttributeMap[]' cannot be converted to type 'IPerson[]'

显然它们是不同的类型,但数据结构完全相同。我的问题是如何才能将发电机

AttributeMap
投射到我的
IPerson
界面?

typescript amazon-dynamodb
7个回答
5
投票

执行此操作的正确方法是使用 AWS DynamoDB SDK

unmarshall
方法。


JavaScript AWS SDK V3(2020 年 12 月后)

使用

unmarshall
包中的
@aws-sdk/util-dynamodb
方法。

文档示例.

const { unmarshall } = require("@aws-sdk/util-dynamodb");

unmarshall(res.Item) as Type;

旁注:AWS DynamoDB JavaScript SDK 提供了

DynamoDBDocumentClient
,它消除了整个问题并使用普通的键值对象。


JavaScript AWS SDK 的先前版本(2020 年 12 月之前)

使用

AWS.DynamoDB.Converter

// Cast the result items to a type.
const people: IPerson[] = result.Items?.map((item) => Converter.unmarshall(item) as IPerson);

unmarshall()
的文档:

解组(数据,选项)⇒映射

将 DynamoDB 记录转换为 JavaScript 对象。

旁注:AWS DynamoDB JavaScript SDK 提供了

DynamoDBDocumentClient
,它消除了整个问题并使用普通的键值对象。


3
投票

我刚刚通过先转换为

unknown
来解决它:

const people: IPerson[] = result.Items as unknown as IPerson[];


1
投票

使用 AttributeMap 扩展 IPerson 接口,如下所示:

interface IPerson extends AttributeMap {
    id: string,
    name: string
}

1
投票

似乎

aws-sdk
没有内置序列化。

到目前为止我所做的如下,效果很好:

interface IPerson {
    id: string,
    name: string
}

// for single object
const person = result?.Item as IPerson;

// for arrays
const people = result?.Items?.map((item) => item as IPerson);

1
投票

我也遇到了类似的问题,但使用的是

get
而不是
scan
。我使用类型保护(又名类型谓词)解决了这个问题。我正在使用 AWS SDK v2,并使用
AWS.DynamoDB.DocumentClient
类型的对象从 DynamoDB 表中读取。我首先尝试按照Steven的回答中的建议使用unmarshall,但这导致了一个空对象。看来我使用的客户端已经为我解组了对象。

在这种情况下,问题简化为标准问题:您有一个类型为

any
的对象,并且您想将其转换为某种类型
IPerson
。执行此操作的标准方法是使用类型谓词。像这样的东西:

function isPerson(obj: any): obj is IPerson{
    if(!obj){
        return false
    }
    if(!isSimpleProperty(obj, "id") || !isString(obj.id)){
        return false
    }
    if(!isSimpleProperty(obj, "name") || !isString(obj.name)){
        return false
    }
    return true
}

function isString(obj: any){
    return (typeof obj === 'string' || obj instanceof String)
}

function isSimpleProperty(obj: any, property: PropertyKey){
    const desc = Object.getOwnPropertyDescriptor(obj, property)
    if(!!desc){
        return (!desc.get && !desc.set)
    }
    return false
}

然后您可以使用过滤器方法来取回所有您想要的正确类型的项目:

const client = new AWS.DynamoDB.DocumentClient();

client.scan(params, (error, result) => {
    const people: IPerson[] | undefined = result.Items?.filter(isPerson)
});

1
投票

不幸的是,

aws-sdk
库没有提供通用接口供您设置响应类型。所以你不能做这样的事情:

const person = await client.get<IPerson>(params).promise();

正如其他人提到的,您可以使用类似的方法来转换类型:

const person = result?.Item as IPerson;

这样做的风险是

result?.Item
可能不是
IPerson
的类型,而你正在欺骗 Typescript 认为它是。

由于 DynamoDB 的无模式性质,数据库中的项目在技术上可以是任何形状,因此它可能不包含您所需的

id
name
属性。因此,谨慎的做法是进行一些检查,以防数据不符合您的预期。

一种方法是使用格式化程序将您的 dynamoDB 对象映射到您的预期响应:

const client = new AWS.DynamoDB.DocumentClient();

interface IPerson {
  id?: string,
  name?: string
}

const format = (data: DynamoDB.DocumentClient.AttributeMap): IPerson {
  return {
    id: data?.id,
    name: data?.name
  };
} 

const person = await client.get(params).promise();
      
const response = format(basket.Item); // maps DynamoDB.DocumentClient.AttributeMap => IPerson

DynamoDB.DocumentClient.AttributeMap
client.get
的响应类型,但扫描或其他客户端方法可能具有不同的响应类型。但是,将客户端的响应映射到您需要的类型仍然遵循相同的原则。


0
投票

您可以使用

class-transformer
。如果您需要不显示某些属性,您可以使用
@Exclude()
进行注释,然后使用
plainToClass
功能。

interface IPerson {
    id: string,
    name: string
    @Exclude()
    password: string
}

// for an object
const person = plainToClass(IPerson, response.Item)

// for an array
const people = response.Items.map((item) => plainToClass(IPerson, item));

或者更简单,使用

Object.assign
:

interface IPerson {
    id: string,
    name: string
}

// for an object
const person = Object.assign(IPerson, response.Item);

//for ann array
const people = response.Items.map((item) => Object.assign(IPerson), item);
© www.soinside.com 2019 - 2024. All rights reserved.