我有一个看起来像这样的数据集合:
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'”。这似乎是由于发现可能找不到某事的事实,但是在我的示例中,只允许您查找总是返回结果的“一个”或“两个”。
如何让打字稿知道它将始终在查找结果中返回结果?
答案 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;
};
答案 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())