断言或推断值为非null



我有一个函数,用来清除传递的数组数据中的空值。使用可选的转换函数将数组元素更改为所需的任意形状。看起来像这个

export const getValidItems = <TItem, TReturn>(
items: Maybe<TItem>[],
transform?: (item: TItem) => TReturn
) => {
const validItems: (TItem | TReturn)[] = [];
items.forEach((item) => {
if (item) {
const outputItem = transform ? transform(item) : item;
if (outputItem) {
validItems.push(outputItem);
}
}
});
return validItems;
};

以下是我对这个功能的使用:

const nodes = getValidItems(edges, ({ node }) => node);

尽管我正在检查getValidItems中的非null值,但当我尝试映射到nodes变量时,typescript会向我发出警告,Object is possibly 'null' or 'undefined'

以前我在另一个方法中有这个逻辑,但我将它抽象为这个辅助函数。当if (item)if (outputItem)检查该方法中的位置时,TS可以正确地识别出这些值是非空的。然而,当我将其移动到getValidItems帮助程序时,错误开始出现。

对于某些上下文,以下是我创建辅助之前的函数

export const getNodes = <TNode>(data?: { edges?: { node?: TNode | null }[] | null }) => {
const { edges } = data || {};
if (!edges) {
return [];
}
const validNodes: TNode[] = [];
edges.forEach(({ node }) => {
if (node) {
validNodes.push(node);
}
});
return validNodes;
};

这是我创建助手后的函数:

export const getNodes = <TNode>(data?: { edges?: { node?: TNode | null }[] | null }) => {
const { edges } = data || {};
if (!edges) {
return [];
}
const nodes = getValidItems(edges, ({ node }) => node);
return nodes
};

在新的getNodes中,typescript似乎无法推断nodes(typeof node)[],并且数组中的项不是空

是否可以断言我的方法的输出不包含null或未定义,或者是否有不同的方法来编写该方法,以便typescript可以推断出这些信息和transform的返回类型?

这里发生了一些事情。

第一个是返回(TItem | TReturn)[],这是一个项目数组,数组中的每个项目都可以是TItem或TReturn。

您可以看到,这使节点的类型变得复杂:({ node?: TNode | null | undefined } | TNode | null | undefined)[]。这是因为根据类型,数组中的每个项都可以是typeof edges[0]或转换函数的返回类型(即TNode | null | undefined(。在这个数组上迭代时,您必须检查每个项,看看它是返回类型还是某条边的类型。

这实际上并不准确,因为您的方法实际上返回TItem[] | TReturn[],或者用英语";TItems的阵列或TReturns的阵列";。但我们可以进一步缩小范围,说它是TItems[],如果没有提供可选的转换函数,并且如果提供了一个,那么它是该函数返回类型的数组。

这可以用函数重载更准确地表示,比如:

export function getValidItems<TItem>(items: Maybe<TItem>[]): TItem[]
export function getValidItems<TItem, TReturn>(items: Maybe<TItem>[], transform: (item: TItem) => TReturn): TReturn[]
export function getValidItems<TItem, TReturn>(items: Maybe<TItem>[], transform?: (item: TItem) => TReturn): TItem[] | TReturn[] {
const validItems: (TItem | TReturn)[] = [];
items.forEach((item) => {
if (item) {
const outputItem = transform ? transform(item) : item;
if (outputItem) {
validItems.push(outputItem);
}
}
});
return validItems as TItem[] | TReturn[];
};

我们现在已经将nodes的类型缩小为(TNode | null | undefined)[]。这准确地推断了转换函数的返回类型,它返回节点(您已经声明为类型TNode | null

因此,getValidItems()函数正在做的另一件事目前没有反映在类型中。它正在检查transform()的结果是否真实,否则将被过滤掉。要键入此类型,我们需要断言生成的数组不是TReturns的数组,而是非空TReturns的数组。我们可以使用内置的NonNullable<T>辅助类型来执行此操作,如下所示:

export function getValidItems<TItem>(items: Maybe<TItem>[]): TItem[]
export function getValidItems<TItem, TReturn>(items: Maybe<TItem>[], transform: (item: TItem) => TReturn): NonNullable<TReturn>[]
export function getValidItems<TItem, TReturn>(items: Maybe<TItem>[], transform?: (item: TItem) => TReturn): TItem[] | TReturn[] {
const validItems: (TItem | TReturn)[] = [];
items.forEach((item) => {
if (item) {
const outputItem = transform ? transform(item) : item;
if (outputItem) {
validItems.push(outputItem);
}
}
});
return validItems as TItem[] | TReturn[];
};

现在,如果您在nodes上迭代,您可以访问属性,而无需typescript警告它可能是nullundefined

你可以在操场上看到这个。

最新更新