typescript 类型脚本数组查找可能未定义

n6lpvg4x  于 2022-12-19  发布在  TypeScript
关注(0)|答案(8)|浏览(202)

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

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;

抛出一个类型脚本错误“Object is possibly 'undefined'”。这似乎是因为find可能找不到某些东西,但在我的示例中,只允许查找“one”或“two”,这将始终返回一个结果。
我如何让typescript知道它总是会在find中返回结果?

dnph8jn4

dnph8jn41#

    • 说明**

您遇到这种情况的原因是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
ddarikpa

ddarikpa2#

如果你绝对肯定你总是会得到一个匹配,那么你可以告诉TS:
使用项目类型Assert:
第一个月
或非空Assert后缀:
const getData = (query: 'one' | 'two') => namedItems.find((item) => query === item.name)!.data;

n53p2ov0

n53p2ov03#

使用筛选器而不是查找:

版本1:

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

版本2:

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)
3phpmpom

3phpmpom4#

**选项1:**最简单的破解方法是使用非空Assert操作符:

  • (但正如@Emanuel Lindström提到的,这是最懒、最丑、最容易出错的黑客)*
const getData = (query: "one" | "two") =>
  namedItems.find((item): boolean => query === item.name)!.data;
                                                         ^

**选项2:**在Typescript 3.7中,这可以通过Assert函数轻松实现:

  • (无需任何修改、转换或变通方法)*
const getData = (query: 'one' | 'two'): string => {
  const item = namedItems.find(({ name }) => query === name);
  assert(item); // (A)

  return item.data; // (B)
}

A行中的Assert函数assert()影响了B行中item的静态类型,使其成为Item
在Node.js中,可以通过the built-in module使用assert()函数:

import assert from 'assert';

在浏览器环境中,需要自己声明它。
例如,在项目的utilities/文件夹中:

function assert(value: unknown): asserts value {}
cidc1ykv

cidc1ykv5#

我如何让typescript知道它总是会在find中返回结果?
最简单、最懒惰、最丑陋和最容易出错的解决方案是使用非空Assert操作符:
第一个月
这是一种告诉typescript该值将始终被定义的方式,但是话又说回来,如果要覆盖它,为什么还要使用typescript呢?您应该"真正"地防范可能出现的未定义值,因为代码可能在将来更改,错误也可能发生。因此,使用之前推荐的任何解决方案,或者我的解决方案:

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

空合并运算符(??)是一种逻辑运算符,当左侧操作数为空或未定义时,它返回右侧操作数,否则返回左侧操作数。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
如果你想要一个默认值,你可以在??-运算符的右边添加任何你想要的值,如果你不想要,你可以做一个错误检查:if(!getData())

iyzzxitl

iyzzxitl6#

Array.find()可能不成功,并可能返回undefined
由于Typescript不知道namedItems数组在运行时不为空(在本例中肯定会失败),因此您无法对其执行任何操作。
但是,您可以使用不同的方法来提取data字段(仅当找到项时),例如,您可以将结果 Package 在数组中,然后Mapif并提取它:

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'));
roqulrg3

roqulrg37#

最简洁的代码可能是这样的:使用可选的链接运算符(?.)并键入Item|任何

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

2izufjch8#

将find的结果设置为any类型的变量|允许其接受空结果的项目

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

See CodePen

相关问题