我有一个switch语句但每个case的内容非常相似,基本上唯一的区别是REST调用中带有select属性的URL,以及当json结果转换为特定类型的特定集合时的.then.
import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http";
import { IWebPartContext } from "@microsoft/sp-webpart-base";
import { IListItem} from "./models/IListItem";
import { IFactory } from "./IFactory";
import { INewsListItem } from "./models/INewsListItem";
import { IDirectoryListItem } from "./models/IDirectoryListItem";
import { IAnnouncementListItem } from "./models/IAnnouncementListItem";
export class ListItemFactory implements IFactory {
// private _listItems: IListItem[];
public getItems(requester: SPHttpClient, siteUrl: string, listName: string): Promise<any[]> {
switch(listName) {
case "GenericList":
let items: IListItem[];
// tslint:disable-next-line:max-line-length
return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Author/Title,Editor/Title&$expand=Author,Editor`,
SPHttpClient.configurations.v1,
{
headers: {
"Accept": "application/json;odata=nometadata",
"odata-version": ""
}
})
.then((response: SPHttpClientResponse): Promise<{ value: IListItem[] }> => {
return response.json();
})
.then((json: { value: IListItem[] }) => {
console.log(JSON.stringify(json.value));
return items=json.value.map((v,i)=>(
{
// key: v.id,
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title
}
));
});
case "News":
let newsitems: INewsListItem[];
// tslint:disable-next-line:max-line-length
return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Created By,Modified By,newsheader,newsbody,expiryDate`,
SPHttpClient.configurations.v1,
{
headers: {
"Accept": "application/json;odata=nometadata",
"odata-version": ""
}
})
.then((response: SPHttpClientResponse): Promise<{ value: INewsListItem[] }> => {
return response.json();
})
.then((json: { value: INewsListItem[] }) => {
return newsitems=json.value.map((v,i)=>(
{
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
newsheader: v.newsheader,
newsbody: v.newsbody,
expiryDate: v.expiryDate
}
));
});
case "Announcements":
let announcementitems: IAnnouncementListItem[];
return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Created,Author/Title,Modified,Editor/Title,announcementBody,expiryDate&$expand=Author,Editor`,
SPHttpClient.configurations.v1,
{
headers: {
"Accept": "application/json;odata=nometadata",
"odata-version": ""
}
})
.then((response: SPHttpClientResponse): Promise<{ value: IAnnouncementListItem[] }> => {
return response.json();
})
.then((json: { value: IAnnouncementListItem[] }) => {
return announcementitems=json.value.map((v,i)=>(
{
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
announcementBody: v.announcementBody,
expiryDate: v.expiryDate
}
));
});
case "Directory":
let directoryitems: IDirectoryListItem[];
return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
SPHttpClient.configurations.v1,
{
headers: {
"Accept": "application/json;odata=nometadata",
"odata-version": ""
}
})
.then((response: SPHttpClientResponse): Promise<{ value: IDirectoryListItem[] }> => {
return response.json();
})
.then((json: { value: IDirectoryListItem[] }) => {
return directoryitems=json.value.map((v,i)=>(
{
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
firstName: v.firstName,
lastName: v.lastName,
mobileNumber: v.mobileNumber,
internalNumber: v.internalNumber
}
));
});
default:
break;
}
}
}
更新1
根据答案,我改变了IFactory.ts
import { IListItem } from "./models/IListItem";
import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http";
import { ListItemFactory } from "./ListItemFactory";
export interface IFactory {
getItems<K extends keyof ListItemFactory['handlers']>(requester: SPHttpClient, siteUrl: string, listName: K): Promise<ReturnType<ListItemFactory['handlers'][K]['process']>> ;
//getItems(requester: SPHttpClient, siteUrl: string, listName: string): Promise<any[]>;
}
和ListItemFactory.ts
import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http";
import { IWebPartContext } from "@microsoft/sp-webpart-base";
import { IListItem} from "./models/IListItem";
import { IFactory } from "./IFactory";
import { INewsListItem } from "./models/INewsListItem";
import { IDirectoryListItem } from "./models/IDirectoryListItem";
import { IAnnouncementListItem } from "./models/IAnnouncementListItem";
export class ListItemFactory implements IFactory {
private handlers = {
GenericList : {
url: (siteUrl: string, listName: string) => `${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Author/Title,Editor/Title&$expand=Author,Editor`,
async process(result: any) {
const json: { value: IListItem[] } = result;
return json.value.map((v,i)=>({
// key: v.id,
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title
}));
}
},
News: {
url: (siteUrl: string, listName: string) => `${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Created By,Modified By,newsheader,newsbody,expiryDate`,
async process(result: any) {
const json: { value: INewsListItem[] } = result;
return json.value.map((v,i)=>(
{
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
newsheader: v.newsheader,
newsbody: v.newsbody,
expiryDate: v.expiryDate
}
));
}
},
Announcements: {
url: (siteUrl: string, listName: string) => `${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Created,Author/Title,Modified,Editor/Title,announcementBody,expiryDate&$expand=Author,Editor`,
async process(result: any) {
const json: { value: IAnnouncementListItem[] } = result;
return json.value.map((v,i)=>({
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
announcementBody: v.announcementBody,
expiryDate: v.expiryDate
}));
}
},
Directory: {
url: (siteUrl: string, listName: string) => `${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
process(result: any) {
const json: { value: IDirectoryListItem[] } = result;
return json.value.map((v,i)=>({
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
firstName: v.firstName,
lastName: v.lastName,
mobileNumber: v.mobileNumber,
internalNumber: v.internalNumber
}));
}
}
};
public async getItems<K extends keyof ListItemFactory['handlers']>(requester: SPHttpClient, siteUrl: string, listName: K): Promise<ReturnType<ListItemFactory['handlers'][K]['process']>> {
var h = this.handlers[listName];
const response = await requester.get(
h.url(siteUrl, listName),
SPHttpClient.configurations.v1,
{
headers: {
"Accept": "application/json;odata=nometadata",
"odata-version": ""
}
});
var json = response.json();
return h.process(json) as any;
}
}
但是我得到了这个错误
[19:42:56] Error - typescript - src/webparts/factoryMethod/components/IFactory.ts(5,122): error TS2304: Cannot find name 'ReturnType'.
[19:42:56] Error - typescript - src/webparts/factoryMethod/components/ListItemFactory.ts(80,135): error TS2304: Cannot find name 'ReturnType'.
[19:42:56] Error - typescript - src/webparts/factoryMethod/components/IFactory.ts(5,122): error TS4057: Return type of method from exported interface has or is using private name 'ReturnType'.
[19:42:56] Error - typescript - src/webparts/factoryMethod/components/ListItemFactory.ts(80,135): error TS4055: Return type of public method from exported class has or is using private name 'ReturnType'.
[19:42:56] Finished subtask 'tslint' after 2.93 s
[19:42:56] Error - 'typescript' sub task errored after 2.06 s
TypeScript error(s) occurred.
最佳答案 我会从每个开关中取出公共部分,并在地图对象中移动不同的部分.不常见的部分似乎是网址以及如何处理结果.
我们也可以使用返回类型和列表类型做得更好.我们将密钥限制为仅受支持的值之一,并且具有特定于传入的listName的返回类型.
export class ListItemFactory implements IFactory {
handlers = {
GenericList : {
url: (siteUrl: string, listName: string) => `${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Author/Title,Editor/Title&$expand=Author,Editor`,
async process(result: any) {
const json: { value: IListItem[] } = result;
return json.value.map((v,i)=>({
// key: v.id,
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title
}));
}
},
News: {
url: (siteUrl: string, listName: string) => `${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Created By,Modified By,newsheader,newsbody,expiryDate`,
async process(result: any) {
const json: { value: INewsListItem[] } = result;
return json.value.map((v,i)=>(
{
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
newsheader: v.newsheader,
newsbody: v.newsbody,
expiryDate: v.expiryDate
}
));
}
},
Announcements: {
url: (siteUrl: string, listName: string) => `${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Created,Author/Title,Modified,Editor/Title,announcementBody,expiryDate&$expand=Author,Editor`,
async process(result: any) {
const json: { value: IAnnouncementListItem[] } = result;
return json.value.map((v,i)=>({
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
announcementBody: v.announcementBody,
expiryDate: v.expiryDate
}));
}
},
Directory: {
url: (siteUrl: string, listName: string) => `${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
process(result: any) {
const json: { value: IDirectoryListItem[] } = result;
return json.value.map((v,i)=>({
id: v.Id,
title: v.Title,
created: v.Created,
createdby: v.Author.Title,
modified: v.Modified,
modifiedby: v.Editor.Title,
firstName: v.firstName,
lastName: v.lastName,
mobileNumber: v.mobileNumber,
internalNumber: v.internalNumber
}));
}
}
}
public async getItems<K extends keyof ListItemFactory['handlers']>(requester: SPHttpClient, siteUrl: string, listName: K): Promise<ReturnType<ListItemFactory['handlers'][K]['process']>> {
var h = this.handlers[listName];
const response = await requester.get(
h.url(siteUrl, listName),
SPHttpClient.configurations.v1,
{
headers: {
"Accept": "application/json;odata=nometadata",
"odata-version": ""
}
});
var json = response.json();
return h.process(json) as any;
}
}
let fact = new ListItemFactory();
let requester!: SPHttpClient;
let siteUrl = ''
var genList = fact.getItems(requester, siteUrl, 'GenericList'); // Promise<Promise<{id: any;title: any;created: any;createdby: any;modified: any;modifiedby: any;}[]>>
var dirList = fact.getItems(requester, siteUrl, 'Directory'); // Promise<{id: any;title: any;created: any;createdby: any;modified: any;modifiedby: any;firstName: any;lastName: any;mobileNumber: any;internalNumber: any;}[]>
编辑
关于getItems签名的一些解释.该方法现在是通用的,类型参数K必须是处理程序对象的关键字.这个限制是使用keyof T完成的,它给出了一个代表T和ListItemFactory [‘handlers’]公钥的类型,它是一个类型查询,并给我们处理程序字段的类型,所以我们得到它所有得到的K扩展keyItemFactory的密钥[ ‘处理’].
现在参数listName只能有’GenericList’,’News’,’Announcements’或’Directory’的值,当我们调用metod时,根据K中传递的值将是表示这些值之一的string literal类型.
我们现在想要将getItems的返回值与传入的listName的进程函数的返回类型联系起来.我们将listName作为K中的字符串文字类型,以便获取处理程序的类型,我们可以使用类型查询:ListItemFactory [‘handlers’] [K]向下钻取以获得我们得到的ListItemFactory [‘handlers’] [K] [‘process’]的过程函数的类型.
要获取此函数类型的返回类型,我们使用内置条件类型ReturnType< T>.返回类型≤()=>数>将是数字,类型执行它的名字暗示如果我们传入一个函数类型,我们得到它的返回值
综合起来,我们得到一个返回类型的Promise< ReturnType< ListItemFactory [‘handlers’] [K] [‘process’]>>
编辑
对于2.4.2,您不能使用条件类型(它们仅在2.8之后可用).
您可以像在原始代码中一样使用any作为返回类型(不理想,但这是我们在2.4.2中可以做的,理想情况下您应该尝试更新到更新的版本):
public async getItems<K extends keyof ListItemFactory['handlers']>(requester: SPHttpClient, siteUrl: string, listName: K): Promise<any>