如何在typescript中将泛型箭头函数的返回类型声明为数组和值



考虑一个以数组、属性名和箭头函数为参数的函数。我想要实现的是,过滤数组,并使用基于长度检查的数组(箭头函数的输出(覆盖上述属性的值。

overwriteFieldWith<T1, T2 extends T1[keyof T1]>(
input: Array<T1>, property: keyof T1, onOverwrite: (i: T1) => T2
): T[] {
// filter the input array and replace the property for
// each item with the output of the supplied arrow function
return input.filter(i => {
const value = onOverwrite(i);
if(value.length) {  // giving me error 'length does not exist on type T2'
i[property] = value;
console.log(i);
return true;
} else {
return false;
}
});
}

当我试图对它进行长度检查时,它会给我错误。提供的箭头函数总是会返回一个数组,但我如何才能满足编译器的要求?

编辑:链接到ts游乐场。

这里有一种方法:

overwriteFieldWith<T, K extends keyof T>(
input: Array<T>,
property: K,
onOverwrite: (i: T) => Extract<T[K], readonly any[]>
): T[] {
return input.filter(i => {
const value = onOverwrite(i);
if (value.length) {
i[property] = value;
return true;
} else {
return false;
}
});
}

我使函数在property的类型K中具有泛型,以便类型检查器跟踪我们正在讨论的属性。通过这种方式,我们可以要求onOverWrite()实际返回可分配给属性值的东西,而不是其他东西。。。我们需要知道这一点,否则CCD_ 4可能不安全。

我还使用Extract<T, U>联合过滤实用程序类型而不是一些通用的U extends T[K],使onOverwrite()的返回类型仅为Extract<T[K], readonly any[]>

(请注意,readonly any[]类型比any[]更宽;我们不关心修改数组,因此不需要存在可变方法(。

这是为了让编译器相信value.length将存在(因为编译器接受Extract<T, U>可分配给TU,所以编译器知道valuereadonly any[](。它还用于要求onOverwrite()只返回属性类型T[K]的那些联合成员,这些联合成员是数组。无论如何,这适用于您的示例用例。


让我们测试一下:

const filtered = a.overwriteFieldWith(elementTypes, 'elements', (i) =>
i?.elements?.filter(data => !data.hide) ?? []);
/* (method) A.overwriteFieldWith<ElementType, "elements">(input: ElementType[], 
property: "elements", onOverwrite: (i: ElementType) => TElement[]
): ElementType[] */

看起来不错。注意,编译器推断TElementTypeK"elements",因此onOverwrite()的返回类型为TElment[],如下所示:

type TK = ElementType['elements'];
//type TK = TElement[] | undefined
type XTK = Extract<ElementType['elements'], readonly any[]>
// type XTK = TElement[]

因此,ElementType可能具有未定义的elements属性,但onOverwrite不能返回undefined

游乐场链接到代码

最新更新