告诉TypeScript编译器的方法Array.prototype.filter从数组中删除某些类型?

时间:2017-03-24 23:28:01

标签: typescript

我试图通过使用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。

4 个答案:

答案 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的结果。