打字稿数组查找可能未定义

时间:2019-02-17 22:19:13

标签: typescript

我有一个看起来像这样的数据集合:

interface Item {
  name: "one" | "two";
  data: string;
}

const namedItems: Item[] = [
  {
    name: "one",
    data: "some data one",
  },
  {
    name: "two",
    data: "some data two",
  },
];

每个项目都有一个名称,值可以是“一个”或“两个”。

然后运行一个数组来查找:

const getData = (query: "one" | "two") =>
  namedItems.find((item): boolean => query === item.name).data;

引发打字错误“对象可能是'undefined'”。这似乎是由于发现可能找不到某事的事实,但是在我的示例中,只允许您查找总是返回结果的“一个”或“两个”。

如何让打字稿知道它将始终在查找结果中返回结果?

7 个答案:

答案 0 :(得分:2)

说明

遇到这种情况的原因是Array.prototype.find的类型签名:

find(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): T | undefined;

如您所见,它总是返回T | undefined。这很有意义,因为定义为Item[]的集合可以包含任意数量的项-包括0。在您的情况下,该集合是完整的,但是在类型级别上它与[{ name: "one", data: "some data one" }]没什么不同甚至是[]

为了以一种类型安全的方式访问item.data,TypeScript将要求您仔细检查是否确实找到了结果。

const lookup = namedItems.find(item => item.name === 'one');

if (lookup === undefined) {
  throw new TypeError('The value was promised to always be there!');
}

console.log(lookup.data);

解决方案

因为从长远来看,这可能变得很麻烦,所以您可能会发现为这种情况创建帮助功能很有用。

function ensure<T>(argument: T | undefined | null, message: string = 'This value was promised to be there.'): T {
  if (argument === undefined || argument === null) {
    throw new TypeError(message);
  }

  return argument;
}

用法:

const getData = (query: "one" | "two") =>
  ensure(namedItems.find(item => query === item.name)).data

答案 1 :(得分:1)

使用过滤器代替查找:

V1:

const results: string[] = namedItems.filter((item: Item) => 
    item.name === "one" | item.name ===  "two")
    .map((item:Item) => item.data)

V2:

const results: string[] = namedItems.filter((item: Item) => 
    ["one","two"].indexOf(item.name) !== -1)
    .map((item:Item) => item.data)

不确定我是否了解您是否只想要一个结果... 在这种情况下

const results: string[] = namedItems.filter(
    (item: Item, index) => ["one", "two"].indexOf(item.name) !== -1 && index === 0)
    .map((item:Item) => item.data)

答案 2 :(得分:1)

如果你绝对确定你总能得到匹配,那么你可以告诉 TS:

const getData = (query: "one" | "two") => (namedItems.find((item) => query === item.name) as Item).data;

答案 3 :(得分:0)

Array.find()可能不会成功,并且可能返回undefined

由于Typescript不知道您的namedItems数组在运行时不是空的(在这种情况下,这是保证的失败),因此您无法对此做任何事情。

但是,仅当找到一个项目时,才可以使用不同的方法来提取data字段,例如,可以将结果包装在数组中,然后映射if并提取它:

const getData = (query: "one" | "two") =>
  [namedItems.find((item): boolean => query === item.name)]
    .map(x => x && x.data).shift();

const namedItems = [
  {
    name: "one",
    data: "some data one",
  },
  {
    name: "two",
    data: "some data two",
  },
];

const getData = (items, query) =>
  [items.find(item => query === item.name)]
    .map(x => x && x.data).shift();

console.log(getData(namedItems, 'one'));
console.log(getData(namedItems, 'two'));
console.log(getData([], 'one'));

答案 4 :(得分:0)

将find的结果设置为 any | Item 类型的变量,以使其可以接受空结果:

const getData = (query: "one" | "two") => {
  let item:any|Item = namedItems.find((item:Item): boolean => query === item.name);
  return item.data;
};

See CodePen

答案 5 :(得分:0)

最简洁的代码可能是这样的: 使用可选的链接运算符(?。),然后键入Item | any

const getData = (query: "one" | "two"):Item|any => namedItems.find((item:Item): boolean => query === item.name)?.data;

答案 6 :(得分:0)

<块引用>

我如何让打字稿知道它总是在 找到了吗?

最简单、最懒惰、最丑陋且最容易出错的解决方案是使用非空断言运算符:

namedItems.find((item): boolean => query === item.name)!.data;

这是一种告诉 typescript 值将始终被定义的方法,但话说回来,如果要覆盖它,为什么还要使用 typescript?您应该“真正地”防范可能的未定义值,因为代码将来可能会更改并且可能会发生错误。因此,请使用之前推荐的任何解决方案或我的解决方案:

const getData = (query: "one" | "two") =>
  (namedItems.find((item) => query === item.name) ?? {name: "undefined", data: "undefined"}).data;
<块引用>

空合并运算符 (??) 是一个逻辑运算符 当其左侧操作数为时,返回其右侧操作数 null 或 undefined,否则返回其左侧操作数。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

如果您想要一个默认值,您可以在 ?? 运算符的右侧添加任何您想要的值。如果没有,您可以进行虚假检查:if(!getData())