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



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

interface IPerson {
id: string,
name: string
}

我在发电机中的人员表上运行表扫描,我希望能够这样做:

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界面?

执行此操作的正确方法是使用 AWS DynamoDB 开发工具包unmarshall方法。


JavaScript AWS 开发工具包 V3(2020 年 12 月发布(

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

文档示例。

const { unmarshall } = require("@aws-sdk/util-dynamodb");
unmarshall(res.Item) as Type;

旁注:AWS DynamoDB JavaScript 开发工具包提供了一个DynamoDBDocumentClient,可以消除整个问题,而是使用普通键值对象。


JavaScript AWS 开发工具包的先前版本(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 开发工具包提供了一个DynamoDBDocumentClient,可以消除整个问题,而是使用普通键值对象。

我只是通过首先强制转换为unknown来解决它:

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

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

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

似乎没有内置的序列化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);

我遇到了类似的问题,但使用的是get而不是scan。我使用类型保护又名类型谓词解决了这个问题。我正在使用 AWS 开发工具包的 v2,并且正在使用类型AWS.DynamoDB.DocumentClient的对象从 DynamoDB 表中读取数据。我首先尝试按照史蒂文的回答中的建议使用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)
});

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

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

正如其他人所提到的,您可以使用以下内容强制转换类型:

const person = result?.Item as IPerson;

这样做的风险是result?.Item可能不是IPerson的类型,你正在欺骗Typescript认为它是。

由于 DynamoDB 的无架构性质,从技术上讲,数据库中的项目可以是任何形状,因此它可能不包含您需要的idname属性。因此,谨慎的做法是进行一些检查,以防数据与预期不符。

一种方法是使用格式化程序将 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.AttributeMapclient.get的响应类型,但扫描或其他客户端方法可能具有不同的响应类型。但是,将客户端的响应映射到所需类型的相同原则仍然适用。

最新更新