简体   繁体   English

Typescript 在不同类型的数组中查找

[英]Typescript find in array with different types

I have an array of objects, each object has different type.我有一个对象数组,每个 object 都有不同的类型。 I am using Array.find (or for loop, no difference) to get one of the objects from array.我正在使用 Array.find(或 for 循环,没有区别)从数组中获取对象之一。 Right now Typescript can not understand what type i am getting from array unless i add additional || reportData.type !== "REGIONS"现在 Typescript 无法理解我从数组中获取的类型,除非我添加额外的|| reportData.type !== "REGIONS" || reportData.type !== "REGIONS" check. || reportData.type !== "REGIONS"检查。 Is there any other way to solve it?还有其他方法可以解决吗?

 export interface IReportItemFactionStatus {
    type: "FACTION_STATUS";
  }

  export interface IReportItemRegions {
    type: "REGIONS";
    regions: [];
  }

  export type IReportItem = IReportItemRegions | IReportItemFactionStatus;

  export type IReport = Array<IReportItem>;

  // ... code ...
  // report has type IReport

  const reportData = report.find((d) => {
      return d.type === "REGIONS";
  });

  if (!reportData) {
    return [];
  }
  console.log(reportData.regions); // Typescript error here

But if i add additional check for reportData.type it starts to work fine但是,如果我为 reportData.type 添加额外的检查,它就会开始正常工作

  if (!reportData || reportData.type !== "REGIONS") {
    return [];
  }
  console.log(reportData.regions); // No Typescript error here :\

Your structure enforces reportData to be of type IReportItem which is : IReportItemRegions or IReportItemFactionStatus because it's an element of report array. 您的结构将reportData的类型设置为IReportItem ,它是: IReportItemRegionsIReportItemFactionStatus因为它是report数组的元素。

You want to display property regions which is only implemented in interface IReportItemRegions . 您想要显示仅在IReportItemRegions接口中实现的属性regions We have no idea if reportData is of type IReportItemRegions . 我们不知道reportData是否为IReportItemRegions类型。 Before trying to access the property, you must ensure your object implements the property : 在尝试访问该属性之前,必须确保您的对象实现了该属性:

if ('regions' in reportData) {
  console.log(reportData.regions)
}

If you want TypeScript to infer the type, you must get rid of find et rewrite your code. 如果希望TypeScript推断类型,则必须摆脱find重写代码。 I've come with a simple implementation : 我提供了一个简单的实现:

let reportData: IReportItemRegions;
let i = 0;
while (!reportData && report.length < i) {
  const currentReportData = report[i];
  if (currentReportData.type === 'REGIONS') {
    // Typescrit knows currentReportData is of type IReportItemRegions
    reportData = currentReportData;
  }
  i++;
}

if (!reportData) {
  return [];
}
console.log(reportData.regions);

Here is one possible solution:这是一种可能的解决方案:

interface IReportItemFactionStatus {
  type: "FACTION_STATUS";
}

interface IReportItemRegions {
  type: "REGIONS";
  regions: readonly string[];
}

type IReportItem = IReportItemRegions | IReportItemFactionStatus;

const report = [
  {
    type: "REGIONS",
    regions: ["region"]
  },
  {
    type: "FACTION_STATUS"
  }
] as const satisfies readonly IReportItem[]

type LookUp<T, TType> = Extract<T, { type: TType }>;

function lookup<TArr extends readonly any[], const TType extends TArr[number]['type']>(arr: TArr, type: TType) {
  return arr.filter((d): d is LookUp<TArr[number], TType> => {
    return d.type === type;
  });
}

const x = lookup(report, "REGIONS")
// {
//     readonly type: "REGIONS";
//     readonly regions: readonly ["region"];
// }[]

const y = lookup(report, "FACTION_STATUS")
//  {
//     readonly type: "FACTION_STATUS";
// }[]

TS throws error as the return value will be of type IReportItemRegions | IReportItemFactionStatus TS引发错误,因为返回值的类型为IReportItemRegions | IReportItemFactionStatus IReportItemRegions | IReportItemFactionStatus . IReportItemRegions | IReportItemFactionStatus

If its of type IReportItemFactionStatus , as there is no regions in it. 如果其类型为IReportItemFactionStatus ,则因为其中没有regions So its should throw an error. 所以它应该抛出一个错误。

When you add this check: reportData.type !== "REGIONS" , you are telling typescript that for cases when you get IReportItemFactionStatus , you are returning before and console.log(reportData.regions); 当您添加以下检查: reportData.type !== "REGIONS" ,您在告诉打字稿说,在得到IReportItemFactionStatus情况下,您将返回before和console.log(reportData.regions); becomes unreachable code. 成为无法访问的代码。 Hence no error. 因此没有错误。


Alternate way: 替代方式:

enum ReportType {
  REGIONS = "REGIONS",
  FACTION_STATUS = "FACTION_STATUS"
}

export interface IReportItem {
  type: ReportType;
  regions?: [];
}

// No need for this now
//export type IReportItem = IReportItemRegions | IReportItemFactionStatus;

export type IReport = Array < IReportItem > ;

// ... code ...
// report has type IReport

const reportData: IReportItem | null = report.find((d) => {
  return d.type === ReportType.REGION;
});

if (!reportData) {
  return [];
}
console.log(reportData.regions); // Typescript error here

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM