代码之家  ›  专栏  ›  技术社区  ›  Luis Valencia

返回接口或扩展接口的数组

  •  2
  • Luis Valencia  · 技术社区  · 8 年前

    不确定标题是否与我的问题正确相似,但我愿意接受建议。

    我有以下方法返回IListItem数组或扩展该接口的其他接口数组。

    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<IListItem[]> {
            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 items=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`,
                    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 items=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 items=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:
                    return null;
            }
          }
    }
    

    界面如下:

    export  interface IListItem {
        [key: string]: any;
        id: string;
        title: string;
        modified: Date;
        created: Date;
        modifiedby: string;
        createdby: string;
    }
    
    
    
    
    import {IListItem} from "./IListItem";
    
    export interface  INewsListItem extends IListItem {
        newsheader: string;
        newsbody: string;
        expiryDate: Date;
    }
    
    import {IListItem} from "./IListItem";
    
    export interface  IAnnouncementListItem extends IListItem {
        announcementBody: string;
        expiryDate: Date;
    }
    
    
    import {IListItem} from "./IListItem";
    
    export interface IDirectoryListItem extends IListItem {
            firstName: string;
            lastName: string;
            mobileNumber: string;
            internalNumber: string;
    }
    

    TSX组件的消费者方法是:

     // read items using factory method pattern and sets state accordingly
      private readItemsAndSetStatus(): void {
        this.setState({
          status: "Loading all items..."
        });
    
        const factory: ListItemFactory = new ListItemFactory();
        factory.getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName)
        .then((items: IListItem[]) => {
          const keyPart: string = this.props.listName === "GenericList" ? "" : this.props.listName;
            // the explicit specification of the type argument `keyof {}` is bad and
            // it should not be required.
            this.setState<keyof {}>({
              status: `Successfully loaded ${items.length} items`,
              ["Details" + keyPart + "ListItemState"] : {
                items
              },
              columns: buildColumns(items)
            });
        });
      }
    

    我粘贴在整个TSX下方仅供参考

    //#region Imports
    import * as React from "react";
    import styles from "./FactoryMethod.module.scss";
    import  { IFactoryMethodProps } from "./IFactoryMethodProps";
    import {
      IDetailsListItemState,
      IDetailsNewsListItemState,
      IDetailsDirectoryListItemState,
      IDetailsAnnouncementListItemState,
      IFactoryMethodState
    } from "./IFactoryMethodState";
    import { IListItem } from "./models/IListItem";
    import { IAnnouncementListItem } from "./models/IAnnouncementListItem";
    import { INewsListItem } from "./models/INewsListItem";
    import { IDirectoryListItem } from "./models/IDirectoryListItem";
    import { escape } from "@microsoft/sp-lodash-subset";
    import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http";
    import { ListItemFactory} from "./ListItemFactory";
    import { TextField } from "office-ui-fabric-react/lib/TextField";
    import {
      DetailsList,
      DetailsListLayoutMode,
      Selection,
      buildColumns,
      IColumn
    } from "office-ui-fabric-react/lib/DetailsList";
    import { MarqueeSelection } from "office-ui-fabric-react/lib/MarqueeSelection";
    import { autobind } from "office-ui-fabric-react/lib/Utilities";
    import PropTypes from "prop-types";
    //#endregion
    
    
    export default class FactoryMethod extends React.Component<IFactoryMethodProps, IFactoryMethodState> {
      constructor(props: IFactoryMethodProps, state: any) {
        super(props);
        this.setInitialState();
      }
    
      // lifecycle help here: https://staminaloops.github.io/undefinedisnotafunction/understanding-react/
    
      //#region Mouting events lifecycle
    
      // the data returned from render is neither a string nor a DOM node.
      // it's a lightweight description of what the DOM should look like.
      // inspects this.state and this.props and create the markup.
      // when your data changes, the render method is called again.
      // react diff the return value from the previous call to render with
      // the new one, and generate a minimal set of changes to be applied to the DOM.
      public render(): React.ReactElement<IFactoryMethodProps> {
        switch(this.props.listName) {
            case "GenericList":
              // tslint:disable-next-line:max-line-length
              return <this.ListMarqueeSelection items={this.state.DetailsListItemState.items} columns={this.state.columns} />;
            case "News":
              // tslint:disable-next-line:max-line-length
              return <this.ListMarqueeSelection items={this.state.DetailsNewsListItemState.items} columns={this.state.columns}/>;
            case "Announcements":
              // tslint:disable-next-line:max-line-length
              return <this.ListMarqueeSelection items={this.state.DetailsAnnouncementListItemState.items} columns={this.state.columns}/>;
            case "Directory":
              // tslint:disable-next-line:max-line-length
              return <this.ListMarqueeSelection items={this.state.DetailsDirectoryListItemState.items} columns={this.state.columns}/>;
            default:
              return null;
        }
      }
    
       // invoked once, only on the client (not on the server), immediately AFTER the initial rendering occurs.
       public componentDidMount(): void {
        // you can access any refs to your children
        // (e.g., to access the underlying DOM representation - ReactDOM.findDOMNode).
        // the componentDidMount() method of child components is invoked before that of parent components.
        // if you want to integrate with other JavaScript frameworks,
        // set timers using setTimeout or setInterval,
        // or send AJAX requests, perform those operations in this method.
        this._configureWebPart = this._configureWebPart.bind(this);
        this.readItemsAndSetStatus();
      }
    
      //#endregion
    
      //#region Props changes lifecycle events (after a property changes from parent component)
      public componentWillReceiveProps(nextProps: IFactoryMethodProps): void {
        if(nextProps.listName !== this.props.listName) {
          this.readItemsAndSetStatus();
        }
      }
    
      //#endregion
    
      //#region private methods
      private _configureWebPart(): void {
        this.props.configureStartCallback();
      }
    
      public setInitialState(): void {
        this.state = {
          type: "ListItem",
          status: this.listNotConfigured(this.props)
            ? "Please configure list in Web Part properties"
            : "Ready",
          columns:[],
          DetailsListItemState:{
            items:[]
          },
          DetailsNewsListItemState:{
            items:[]
          },
          DetailsDirectoryListItemState:{
            items:[]
          },
          DetailsAnnouncementListItemState:{
            items:[]
          },
        };
      }
    
      // reusable inline component
      private ListMarqueeSelection = (itemState: {columns: IColumn[], items: IListItem[] }) => (
          <div>
              <DetailsList
                items={ itemState.items }
                columns={ itemState.columns }
                setKey="set"
                layoutMode={ DetailsListLayoutMode.fixedColumns }
                selectionPreservedOnEmptyClick={ true }
                compact={ true }>
              </DetailsList>
          </div>
      )
    
      // read items using factory method pattern and sets state accordingly
      private readItemsAndSetStatus(): void {
        this.setState({
          status: "Loading all items..."
        });
    
        const factory: ListItemFactory = new ListItemFactory();
        factory.getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName)
        .then((items: IListItem[]) => {
          const keyPart: string = this.props.listName === "GenericList" ? "" : this.props.listName;
            // the explicit specification of the type argument `keyof {}` is bad and
            // it should not be required.
            this.setState<keyof {}>({
              status: `Successfully loaded ${items.length} items`,
              ["Details" + keyPart + "ListItemState"] : {
                items
              },
              columns: buildColumns(items)
            });
        });
      }
    
      private listNotConfigured(props: IFactoryMethodProps): boolean {
        return props.listName === undefined ||
          props.listName === null ||
          props.listName.length === 0;
      }
    
      //#endregion
    }
    

    问题是它总是在列表项数组中返回,因此react组件只显示IListItem的列,而不显示其他接口的列

    更新1:

    尝试在THEN中执行强制转换将导致变量myItems出现重复警告。

    enter image description here

    1 回复  |  直到 8 年前
        1
  •  2
  •   Connor Dickson    8 年前

    这是一个问题,因为你的承诺正在返回IListItem[]。

    如果将工厂中getItems方法中承诺的返回类型更改为任意[]。然后对使用者中getItems方法调用的“Then”中的返回类型执行相同的操作。然后在使用者中的listName上执行另一个开关,以转换为所需的类型,在那里它应该可以工作

    你可以 cast 使用此选项是因为您在消费者中有可用的列表名称;

    var myItems = null; switch(listName) { case "GenericList": myItems = items as IListItem[]; case "News": myItems = items as INewsListItem[]; case "Announcements": myItems = items as IAnnouncementListItem[]; case "Directory": myItems = items as IDirectoryListItem[]; }

    推荐文章