Reactjs 类型为'的参数;{状态:字符串;详细信息列表项状态:{items:IListItem[];列:未定义

Reactjs 类型为'的参数;{状态:字符串;详细信息列表项状态:{items:IListItem[];列:未定义,reactjs,typescript,design-patterns,Reactjs,Typescript,Design Patterns,我有以下错误,下面我将尝试解释我试图实现的目标: [ts] Argument of type '{ status: string; DetailsAnnouncementListItemState: { items: IListItem[]; columns: undefined[]; }...' is not assignable to parameter of type 'Pick<IFactoryMethodState, "status" | "DetailsAnnouncement

我有以下错误,下面我将尝试解释我试图实现的目标:

[ts]
Argument of type '{ status: string; DetailsAnnouncementListItemState: { items: IListItem[]; columns: undefined[]; }...' is not assignable to parameter of type 'Pick<IFactoryMethodState, "status" | "DetailsAnnouncementListItemState">'.
  Types of property 'DetailsAnnouncementListItemState' are incompatible.
    Type '{ items: IListItem[]; columns: undefined[]; }' is not assignable to type 'IDetailsAnnouncementListItemState'.
      Types of property 'items' are incompatible.
        Type 'IListItem[]' is not assignable to type 'IAnnouncementListItem[]'.
          Type 'IListItem' is not assignable to type 'IAnnouncementListItem'.
            Property 'announcementBody' is missing in type 'IListItem'.
现在,我有一个工厂方法设计模式,如下所示:

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 IDirectoryListItem extends IListItem {
        firstName: string;
        lastName: string;
        mobileNumber: string;
        internalNumber: string;  
}

import {IListItem} from './IListItem';

export interface  IAnnouncementListItem extends IListItem {
    announcementBody: string;
    expiryDate: Date;  
}
import { IListItem } from './models/IListItem';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
export  interface IFactory{
    getItems(requester: SPHttpClient, siteUrl: string, listName: string): 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{   
    getItems(requester: SPHttpClient, siteUrl: string, listName: string): IListItem[] {
        switch(listName) {
            case 'List':
                let items: IListItem[];
                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: IListItem[] }> => {
                    return response.json();
                })
                .then((response: { value: IListItem[] }): void => {
                    items= response.value;
                });
                return items;
            case 'News':
                let newsitems: INewsListItem[];
                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: INewsListItem[] }> => {
                    return response.json();
                })
                .then((response: { value: INewsListItem[] }): void => {
                    newsitems= response.value;
                });
                return newsitems;
            case 'Announcements':
                let announcementitems: IAnnouncementListItem[];
                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((response: { value: IAnnouncementListItem[] }): void => {
                    announcementitems= response.value;
                });
                return announcementitems;
            case 'Directory':
                let directoryitems: IDirectoryListItem[];
                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((response: { value: IDirectoryListItem[] }): void => {
                    items= response.value;
                });
                return directoryitems;
            default:
                return null;
        }
      }
} 
这是我得到异常的地方,第一个开关情况工作正常,第二个开关情况不正常,我理解发生了什么,getitems返回ListItem[],但我正在尝试将该值分配给一个AnnouncementListItem[]

但是,我不确定如何修复它,因为您可以看到newslisitem扩展了ListItem,我希望使此解决方案尽可能通用

更新

忘了这个文件

import { IListItem } from './models/IListItem';
import { INewsListItem } from './models/INewsListItem';
import { IDirectoryListItem } from './models/IDirectoryListItem';
import { IAnnouncementListItem } from './models/IAnnouncementListItem';
import {
  IColumn
} from 'office-ui-fabric-react/lib/DetailsList';

export interface IFactoryMethodState{
  type: string;
  status: string;
  DetailsListItemState: IDetailsListItemState;
  DetailsNewsListItemState: IDetailsNewsListItemState;
  DetailsDirectoryListItemState : IDetailsDirectoryListItemState;
  DetailsAnnouncementListItemState : IDetailsAnnouncementListItemState;
}

export interface IDetailsListItemState {
  columns: IColumn[];
  items: IListItem[];
}

export interface IDetailsNewsListItemState {
  columns: IColumn[];
  items: INewsListItem[];
}

export interface IDetailsDirectoryListItemState {
  columns: IColumn[];
  items: IDirectoryListItem[];
}

export interface IDetailsAnnouncementListItemState {
  columns: IColumn[];
  items: IAnnouncementListItem[];
}

在接下来的内容中,我将只讨论
IANNounceListItem
,但是您可以对每个子类型使用相同的分析


该错误正确地告诉您编译器无法验证
factory.getItems()
返回一个
iannounoconnectListItem
数组。正如您所说,
iannounoconnectListItem
扩展了
iannounoconnectListItem
,这意味着每个
iannounoconnectListItem
都是
iannounoconnectListItem
,但不是每个
IListItem
都是
iannounoconnectListItem
。因此它警告您不要这样做通过将
IListItem
数组视为
iAnonounceListItem
数组来处理不安全的内容

有多种方法可以解决这一问题:


类型断言 一种方法是通过断言
factory.getItems()
的返回值是正确的类型,告诉编译器您知道自己在做什么,而且不必担心

let announcementlistItems  = factory.getItems(
  this.props.spHttpClient, this.props.siteUrl, this.props.listName
) as IAnnouncementListItem[];  
这会使编译器静音,但会失去编译器类型检查的好处


运行时类型保护 另一种方法是从
getItems()
获取结果,并执行运行时检查,以查看返回值是否实际上是
IAnnounceListItem[]
的数组

function isArrayOfIAnnouncementListItem(arr: IListItem[]): arr is IAnnouncementListItem[] {
  return arr.every(listItem => 'announcementBody' in listItem);
}
然后

let announcementlistItems  = factory.getItems(
  this.props.spHttpClient, this.props.siteUrl, this.props.listName
);
if (!isArrayOfIAnnouncementListItem(announcementListItems)) {
  throw new Error("Wrong Type or Something");
}
// now announcementListItems is known to be IAnnouncementListItem[]
这将使编译器感到高兴。这比简单的断言更安全,但您仍在执行运行时检查。如果其他人实现了
getItems()
,这可能是您所能做的最好的事情。但由于您正在实现它,您实际上可以使
getItems()
本身更安全:


重载签名 处理此问题的最佳方法可能是更改
getItems()
的签名,以便它知道
listName
参数会影响输出类型。这可以通过以下方法完成:

(如果需要,您可以通过泛型而不是重载获得此行为;根据要求,可以获得更多信息)

现在,当您调用
getItems()
时,您将被限制使用可接受值集中的
listName
,并且返回类型将为您缩小


所以,如果你愿意,你可以做任何一个。希望这有帮助。祝你好运

let announcementlistItems  = factory.getItems(
  this.props.spHttpClient, this.props.siteUrl, this.props.listName
);
if (!isArrayOfIAnnouncementListItem(announcementListItems)) {
  throw new Error("Wrong Type or Something");
}
// now announcementListItems is known to be IAnnouncementListItem[]
// overloads
getItems(requester: SPHttpClient, siteUrl: string, listName: "Announcements"): IAnnouncementListItem[];
// put other overloads for each allowable type here

// implementation
getItems(requester: SPHttpClient, siteUrl: string, listName: string): IListItem[] | null; { // .. implementation