考虑一个以数组、属性名和箭头函数为参数的函数。我想要实现的是,过滤数组,并使用基于长度检查的数组(箭头函数的输出(覆盖上述属性的值。
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>
可分配给T
和U
,所以编译器知道value
是readonly 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[] */
看起来不错。注意,编译器推断T
为ElementType
,K
为"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
。
游乐场链接到代码