我试图通过使用Array.prototype.filter从数组中过滤null(undefined)元素,但是TypeScript编译器似乎无法识别“过滤器”函数的派生数组并且无法通过类型检查。
假设遵循简化代码,其中我有一个带有(number | undefined)[]类型的数组,并希望过滤undefined以适合数字[]数组。
const arry = [1, 2, 3, 4, "5", 6];
const numArry: number[] = arry
.map((i) => {
return typeof i === "number" ? i : void 0;
})
.filter((i) => i);
错误说:
类型'(数字|未定义)[]'不能分配给'number []'。 输入'号码| undefined'不能分配给'number'类型。 类型'undefined'不能指定为'number'类型。
我可以将结果数组转换为数字[],如下所示,知道过滤器函数删除未定义。
const arry = [1, 2, 3, 4, "5", 6];
const numArry: number[] = (arry
.map((i) => {
return typeof i === "number" ? i : void 0;
})
.filter((i) => i) as Number[]);
除了演员之外,还有更好的方法来实现这一目标吗?
环境:启用了strictNullChecks的TSC2.1。
答案 0 :(得分:6)
使用内置filter
函数无法实现,它被声明为返回与其接收的类型完全相同的数组。
但是,可以定义自己的,完全类型安全的过滤器函数,它接受一个数组和一个user-defined type guard function,并返回一个不同类型的数组。
不确定有用,但现在是:
function typeFilter<T, R extends T>(a: T[], f: (e: T) => e is R): R[] {
const r: R[] = [];
a.forEach(e => { if (f(e)) r.push(e) });
return r;
}
可以像这样使用:
const arry = [1, 2, 3, 4, "5", 6];
function isNumber(e): e is number {
return typeof e === 'number';
}
const numArry: number[] = typeFilter(arry, isNumber);
不幸的是,isNumber()
必须被定义为单独的显式类型函数,因为编译器不够聪明,无法识别内联函数e => typeof e === 'number'
也是类型保护。
答案 1 :(得分:6)
解决方案
创建类型后卫:
function isDefined<T>(argument: T | undefined): argument is T {
return argument !== undefined
}
将其用作类型谓词:
const foo: number[] = [1, 2, undefined, 4].filter(isDefined)
说明
Array.prototype.filter
有一些重载。其中之一了解返回值将取决于您的谓词函数。它使用类型保护:
filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
使用适当的类型保护(而不是使用快捷方式并依靠隐式强制)有助于TypeScript选择此特定的重载。
答案 2 :(得分:3)
map(...)
函数签名是:
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
在您的情况下,通用类型U
将为:number | undefined
filter(...)
签名是:
filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[];
由于T
来自数组接口签名(Array<T>
),因此返回类型将是value
参数的相同类型的数组(泛型类型{{1 }})。在您的情况下T
。
这就是您的返回类型为number | undefined
。
基于此,您需要使用强制转换表达式。如果您不想要此行为,则可以删除number | undefined
标记。
答案 3 :(得分:1)
使用TypeScrpt的用户定义的Type Guard功能:
const arry = [1, 2, 3, 4, "5", 6];
const numArry: number[] = arry
.filter((i): i is number => {
return typeof i === "number";
});
// numArry = [1, 2, 3, 4, 6]
在回调函数中查找i is number
。
这个技巧使我们能够转换类型为Array.filter的结果。