我有一个函数,用来清除传递的数组数据中的空值。使用可选的转换函数将数组元素更改为所需的任意形状。看起来像这个
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()
的结果是否真实,否则将被过滤掉。要键入此类型,我们需要断言生成的数组不是TReturn
s的数组,而是非空TReturn
s的数组。我们可以使用内置的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警告它可能是null
或undefined
。
你可以在操场上看到这个。