我在同一个项目中使用TypeScript和Contentful,并且在如何以一种干净,明智和动态的方式在接口中MapAPI响应方面遇到了问题,因为Contentful的API响应是巨大而丑陋的-对于每种不同的内容类型,它们可能是不同的。
这些是我的主要问题:
1.实际上在一个接口中Map出API响应--它如此之大,有如此多不必要的数据,我需要把它们都放在一个接口中吗?
1.如果客户端后来创建了新的内容类型,那么接下来的API响应的结构会略有不同,此时接口会出错,我该怎么办?
感谢所有的帮助,真的只是需要建议下来的正确方式来调查的解决方案。
这是一个内容丰富的API响应的示例:
{
"sys":{
"type":"Array"
},
"total":2,
"skip":0,
"limit":100,
"items":[
{
"metadata":{
"tags":[
]
},
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"rikydtrxnc79"
}
},
"id":"1w9AMDdCqRHRMEfzif3J0V",
"type":"Entry",
"createdAt":"2023-06-22T14:17:28.969Z",
"updatedAt":"2023-06-22T14:17:28.969Z",
"environment":{
"sys":{
"id":"master",
"type":"Link",
"linkType":"Environment"
}
},
"revision":1,
"contentType":{
"sys":{
"type":"Link",
"linkType":"ContentType",
"id":"blogPost"
}
},
"locale":"en-US"
},
"fields":{
"title":"Second Test",
"body":{
"data":{
},
"content":[
{
"data":{
},
"content":[
{
"data":{
},
"marks":[
],
"value":"This is a test.",
"nodeType":"text"
}
],
"nodeType":"paragraph"
}
],
"nodeType":"document"
},
"tags":[
{
"sys":{
"type":"Link",
"linkType":"Entry",
"id":"5vuJaOnrVvh34oAiBt8qgh"
}
},
{
"sys":{
"type":"Link",
"linkType":"Entry",
"id":"6feqnd9taR55ruluGBsp8h"
}
}
]
}
},
{
"metadata":{
"tags":[
]
},
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"rikydtrxnc79"
}
},
"id":"32eWu0P7zXgbxqum5nbdqP",
"type":"Entry",
"createdAt":"2023-06-22T14:14:34.053Z",
"updatedAt":"2023-06-22T14:14:34.053Z",
"environment":{
"sys":{
"id":"master",
"type":"Link",
"linkType":"Environment"
}
},
"revision":1,
"contentType":{
"sys":{
"type":"Link",
"linkType":"ContentType",
"id":"blogPost"
}
},
"locale":"en-US"
},
"fields":{
"title":"Test",
"body":{
"data":{
},
"content":[
{
"data":{
},
"content":[
{
"data":{
},
"marks":[
],
"value":"This is a test",
"nodeType":"text"
}
],
"nodeType":"heading-1"
},
{
"data":{
},
"content":[
{
"data":{
},
"marks":[
],
"value":"",
"nodeType":"text"
}
],
"nodeType":"paragraph"
},
{
"data":{
},
"content":[
{
"data":{
},
"marks":[
],
"value":"This is a test",
"nodeType":"text"
}
],
"nodeType":"heading-3"
},
{
"data":{
},
"content":[
{
"data":{
},
"marks":[
],
"value":"",
"nodeType":"text"
}
],
"nodeType":"paragraph"
},
{
"data":{
},
"content":[
{
"data":{
},
"marks":[
],
"value":"This is a test.",
"nodeType":"text"
}
],
"nodeType":"paragraph"
},
{
"data":{
},
"content":[
{
"data":{
},
"marks":[
],
"value":"",
"nodeType":"text"
}
],
"nodeType":"paragraph"
}
],
"nodeType":"document"
},
"tags":[
{
"sys":{
"type":"Link",
"linkType":"Entry",
"id":"63QnQZsSKUUI3TNAvsI19J"
}
},
{
"sys":{
"type":"Link",
"linkType":"Entry",
"id":"1O7OQ1YFLMshW7BwBTL3ER"
}
}
],
"images":[
{
"sys":{
"type":"Link",
"linkType":"Asset",
"id":"35LgxsKv96DyW51Uu8DrnM"
}
},
{
"sys":{
"type":"Link",
"linkType":"Asset",
"id":"4bxRR1bBIknnVzRjeqvUIr"
}
}
]
}
}
],
"includes":{
"Entry":[
{
"metadata":{
"tags":[
]
},
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"rikydtrxnc79"
}
},
"id":"1O7OQ1YFLMshW7BwBTL3ER",
"type":"Entry",
"createdAt":"2023-06-22T14:13:23.918Z",
"updatedAt":"2023-06-22T14:13:23.918Z",
"environment":{
"sys":{
"id":"master",
"type":"Link",
"linkType":"Environment"
}
},
"revision":1,
"contentType":{
"sys":{
"type":"Link",
"linkType":"ContentType",
"id":"countryTag"
}
},
"locale":"en-US"
},
"fields":{
"countryTag":"usa"
}
},
{
"metadata":{
"tags":[
]
},
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"rikydtrxnc79"
}
},
"id":"5vuJaOnrVvh34oAiBt8qgh",
"type":"Entry",
"createdAt":"2023-06-22T10:50:36.561Z",
"updatedAt":"2023-06-22T10:50:36.561Z",
"environment":{
"sys":{
"id":"master",
"type":"Link",
"linkType":"Environment"
}
},
"revision":1,
"contentType":{
"sys":{
"type":"Link",
"linkType":"ContentType",
"id":"countryTag"
}
},
"locale":"en-US"
},
"fields":{
"countryTag":"australia"
}
},
{
"metadata":{
"tags":[
]
},
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"rikydtrxnc79"
}
},
"id":"63QnQZsSKUUI3TNAvsI19J",
"type":"Entry",
"createdAt":"2023-06-22T14:13:11.295Z",
"updatedAt":"2023-06-22T14:13:11.295Z",
"environment":{
"sys":{
"id":"master",
"type":"Link",
"linkType":"Environment"
}
},
"revision":1,
"contentType":{
"sys":{
"type":"Link",
"linkType":"ContentType",
"id":"regionTag"
}
},
"locale":"en-US"
},
"fields":{
"title":"americas"
}
},
{
"metadata":{
"tags":[
]
},
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"rikydtrxnc79"
}
},
"id":"6feqnd9taR55ruluGBsp8h",
"type":"Entry",
"createdAt":"2023-06-22T10:41:10.693Z",
"updatedAt":"2023-06-22T10:41:10.693Z",
"environment":{
"sys":{
"id":"master",
"type":"Link",
"linkType":"Environment"
}
},
"revision":1,
"contentType":{
"sys":{
"type":"Link",
"linkType":"ContentType",
"id":"regionTag"
}
},
"locale":"en-US"
},
"fields":{
"title":"apac"
}
}
],
"Asset":[
{
"metadata":{
"tags":[
]
},
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"rikydtrxnc79"
}
},
"id":"35LgxsKv96DyW51Uu8DrnM",
"type":"Asset",
"createdAt":"2023-06-22T14:14:11.979Z",
"updatedAt":"2023-06-22T14:14:11.979Z",
"environment":{
"sys":{
"id":"master",
"type":"Link",
"linkType":"Environment"
}
},
"revision":1,
"locale":"en-US"
},
"fields":{
"title":"Test1",
"description":"",
"file":{
"url":"//images.ctfassets.net/rikydtrxnc79/35LgxsKv96DyW51Uu8DrnM/d8a11b6dc57a416a143af10b3569a7e0/pexels-photo-3844788.jpeg",
"details":{
"size":83381,
"image":{
"width":500,
"height":667
}
},
"fileName":"pexels-photo-3844788.jpeg",
"contentType":"image/jpeg"
}
}
},
{
"metadata":{
"tags":[
]
},
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"rikydtrxnc79"
}
},
"id":"4bxRR1bBIknnVzRjeqvUIr",
"type":"Asset",
"createdAt":"2023-06-22T14:14:26.979Z",
"updatedAt":"2023-06-22T14:14:26.979Z",
"environment":{
"sys":{
"id":"master",
"type":"Link",
"linkType":"Environment"
}
},
"revision":1,
"locale":"en-US"
},
"fields":{
"title":"Test2",
"description":"",
"file":{
"url":"//images.ctfassets.net/rikydtrxnc79/4bxRR1bBIknnVzRjeqvUIr/4ffca3cdb4db84b6ee2129cf05f06cdc/premium_photo-1661964088064-dd92eaaa7dcf.jpeg",
"details":{
"size":46936,
"image":{
"width":1000,
"height":714
}
},
"fileName":"premium_photo-1661964088064-dd92eaaa7dcf.jpeg",
"contentType":"image/jpeg"
}
}
}
]
}
}
现在items数组和includes数组在对象中可能有不同的数据集,所以这就是为什么我很难理解如何解决新内容类型和现有接口/模式不适用的问题。
我已经画出了这样的React,但认为它过度?
export interface ContentfulApiResponse {
sys: {
type: string;
};
total: number;
skip: number;
limit: number;
items: [
{
metadata: {
tags: [];
};
sys: {
space: {
sys: {
type: string;
linkType: string;
id: string;
};
};
id: string;
type: string;
createdAt: string;
updatedAt: string;
environment: {
sys: {
id: string;
type: string;
linkType: string;
};
};
revision: number;
contentType: {
sys: {
type: string;
linkType: string;
id: string;
};
};
locale: string;
};
fields: {
title: string;
body: {
data: {};
content: [
{
data: {};
content: [
{
data: {};
marks: [];
value: string;
nodeType: string;
}
];
nodeType: string;
}
];
nodeType: string;
};
tags: [
{
sys: {
type: string;
linkType: string;
id: string;
};
},
{
sys: {
type: string;
linkType: string;
id: string;
};
}
];
};
},
{
metadata: {
tags: [];
};
sys: {
space: {
sys: {
type: string;
linkType: string;
id: string;
};
};
id: string;
type: string;
createdAt: string;
updatedAt: string;
environment: {
sys: {
id: string;
type: string;
linkType: string;
};
};
revision: number;
contentType: {
sys: {
type: string;
linkType: string;
id: string;
};
};
locale: string;
};
fields: {
title: string;
body: {
data: {};
content: [
{
data: {};
content: [
{
data: {};
marks: [];
value: string;
nodeType: string;
}
];
nodeType: string;
},
{
data: {};
content: [
{
data: {};
marks: [];
value: string;
nodeType: string;
}
];
nodeType: string;
},
{
data: {};
content: [
{
data: {};
marks: [];
value: string;
nodeType: string;
}
];
nodeType: string;
},
{
data: {};
content: [
{
data: {};
marks: [];
value: "";
nodeType: string;
}
];
nodeType: string;
},
{
data: {};
content: [
{
data: {};
marks: [];
value: string;
nodeType: string;
}
];
nodeType: string;
},
{
data: {};
content: [
{
data: {};
marks: [];
value: string;
nodeType: string;
}
];
nodeType: string;
}
];
nodeType: string;
};
tags: [
{
sys: {
type: string;
linkType: string;
id: string;
};
},
{
sys: {
type: string;
linkType: string;
id: string;
};
}
];
images: [
{
sys: {
type: string;
linkType: string;
id: string;
};
},
{
sys: {
type: string;
linkType: string;
id: string;
};
}
];
};
}
];
includes: {
Entry: ContentfulIncludesEntry[];
Asset: ContentfulIncludesAsset[];
};
}
interface ContentfulIncludesAsset {
metadata: {
tags: [];
};
sys: {
space: {
sys: {
type: string;
linkType: string;
id: string;
};
};
id: string;
type: string;
createdAt: string;
updatedAt: string;
environment: {
sys: {
id: string;
type: string;
linkType: string;
};
};
revision: number;
locale: string;
};
fields: {
title: string;
description: string;
file: {
url: string;
details: {
size: number;
image: {
width: number;
height: number;
};
};
fileName: string;
contentType: string;
};
};
}
interface ContentfulIncludesEntry {
metadata: {
tags: [];
};
sys: {
space: {
sys: {
type: string;
linkType: string;
id: string;
};
};
id: string;
type: string;
createdAt: string;
updatedAt: string;
environment: {
sys: {
id: string;
type: string;
linkType: string;
};
};
revision: number;
contentType: {
sys: {
type: string;
linkType: string;
id: string;
};
};
locale: string;
};
fields: {
countryTag?: string;
regionTag?: string;
};
}
请记住,fields对象可以随每种内容类型而变化。
1条答案
按热度按时间zqry0prt1#
我的第一个想法是,您应该从为每个属性名创建一个公共类型开始。Sys、Metadata、Tag、Environment等,完全忽略了它们会有循环引用的事实,这可能会阻止它们编译,因为在这个阶段,你只想把它都弄下来并理解它。例如:
另一个想法是甚至不尝试强类型的服务器响应。相反,接受响应是
unknown
,然后使用zod将数据Assert为代码所需的类型。这样做的一个优点是,运行时Assert将以单元测试和静态类型检查根本无法做到的方式帮助您发现错误和意外情况。但是,你确定你需要自己做这些事情吗?如果像@bgschiller/contentful-typescript-codegen这样的东西没有帮助,我希望你能找到一些你可以在那里使用的东西?
(Note:我不推荐使用@bgschiller/contentful-typescript-codegen。我以前从未听说过它,只是通过在npmjs.com上搜索“types/contentful”才找到它。