Búsqueda de sitios web

¿Manera de decirle al compilador de TypeScript que Array.prototype.filter elimina ciertos tipos de un arreglo?


Estoy intentando filtrar elementos nulos (indefinidos) de un arreglo usando Array.prototype.filter, pero el compilador de TypeScript no parece reconocer el arreglo derivado de la función "filter" y falla en pasar la verificación de tipos.

Suponiendo el siguiente código simplificado donde tengo un arreglo con tipos (number|undefined)[] y quiero filtrar los undefined para ajustarlo a un arreglo 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);

El error dice:

Tipo '(number | undefined)[]' no es asignable al tipo 'number[]'. Tipo 'number | undefined' no es asignable al tipo 'number'. Tipo 'undefined' no es asignable al tipo 'number'.

Puedo convertir el array resultante a number[] como se muestra a continuación, sabiendo que la función filter elimina 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) as Number[]);

¿Hay una manera mejor de lograr esto que no sea mediante un casting?

Entorno: TSC2.1 con strictNullChecks habilitado.

Respuesta #1

Utiliza la funcionalidad de Guardias de Tipo Definidos por el Usuario de TypeScript:

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]

Echa un vistazo a i es número en la función de callback. Este truco nos da la capacidad de convertir un tipo del resultado de Array.filter.

Respuesta #2

Solución

Crea un type guard:

function isDefined<T>(argument: T | undefined): argument is T {
    return argument !== undefined
}

Úsalo como tu predicado de tipo:

const foo: number[] = [1, 2, undefined, 4].filter(isDefined)

Explicación

Array.prototype.filter tiene varias sobrecargas. Una de ellas entiende que el valor de retorno dependerá de tu función de predicado. Utiliza una guardia de tipo:

filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];

Usar una comprobación de tipo adecuada (en lugar de tomar un atajo y depender de la coerción implícita) ayuda a TypeScript a seleccionar esta sobrecarga específica.

Respuesta #3

La forma más fácil de filtrar valores de un array de manera segura para tipos que Typescript entienda es usar flatMap en lugar de map y devolver un array vacío para cualquier cosa que quieras eliminar.

Ejemplo :

const myArray = [1,2,3,4,"5",6,7, undefined,9, 0]
const filteredArray = arr.flatMap(val => typeof val === "number" ? val : [])
// filteredArray: [ 1, 2, 3, 4, 6, 7, 9, 0 ]

flatMap funciona en dos pasos. Primero recorre el arreglo y ejecuta cualquier función que le hayas dado. Luego aplana cualquier arreglo que haya en él.

  1. mapear sobre el arreglo y obtener: [ 1, 2, 3, 4, [], 6, 7, [], 9, 0 ]

  2. Aplana cualquier arreglo en el arreglo: [ 1, 2, 3, 4, 6, 7, 9, 0 ]

¡Voilà! Ahora tienes un array tipado y TypeScript está contento.

Artículos relacionados