Soy nuevo en TypeScript y acabo de comenzar a migrar mi aplicación de JavaScript a TypeScript.
calcAvg , toma una matriz de números y devuelve su promedio.avgRoasDiscrepancy , toma dos matrices, si las dos matrices tienen la misma longitud y no tienen valores nulos, restará cada elemento de una lista con el elemento en el mismo índice de la segunda matriz. El problema está en la siguiente función de map (ver el código completo a continuación):
const discArray = clientRoasArray.map((value: number, index: number) => value - roasArray[index]);Obtuve el siguiente error:
Argument of type '(value: number, index: number) => number' is not assignable to parameter of type '(value: number | null, index: number, array: (number | null)[]) => number'. Types of parameters 'value' and 'value' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number' No sé cómo "convencer" a TypeScript de que tanto value como roasArray[index] son, de hecho, números y no valores nulos.
Aquí está el código completo:
interface DailyData { id: number clientCost:number conValue:number clientConvValue:number margin: number } const calcAvg = (dataList: number[]) => { const reducer = (accumulator: number, curr: number) => accumulator + curr; const dataSum = dataList.reduce(reducer, 0); return dataSum / dataList.length; }; const avgRoasDiscrepancy = (advertiserStats: DailyData[]) => { const clientRoasArray = advertiserStats.map((obj: DailyData) => obj.clientCost > 0 ? obj.clientConvValue/obj.clientCost: null); const roasArray = advertiserStats.map((obj: DailyData) => obj.clientCost > 0 ? obj.conValue/obj.clientCost: null); if (clientRoasArray.length === 0 || roasArray.length === 0) { return 'no data' } if (!!clientRoasArray.every((el: number|null) => el!== null) || (!roasArray.every((el: number|null) => el!== null) )) { //if one of the arrays has null values don't calculate return 'no data'; } if (clientRoasArray.length === roasArray.length) { const discArray = clientRoasArray.map((value: number, index: number) => value - roasArray[index]); return calcAvg(discArray as number[]); } return 'no data'; };en esta línea
const clientRoasArray = advertiserStats.map((obj: DailyData) => obj.clientCost > 0 ? obj.clientConvValue/obj.clientCost: null); Está suponiendo que los elementos de clientRoasArray podrían ser number o podrían ser null y esto está claro en su condición : null
Y en esta línea, está identificando el valor de clientRoasArray solo como un number only , lo cual es incorrecto porque le dijo a su código que el valor podría ser number|null
Todo lo que necesitas es cambiar esta línea
const discArray = clientRoasArray.map((value: number, index: number) => value - roasArray[index]);a esto
const discArray = clientRoasArray.map((value: number | null, index: number) => value - roasArray[index]); pero enfrentará un problema con roasArray[index] porque también podría ser null
No pude hacer que la inferencia de tipo funcionara correctamente usando every las llamadas en las matrices, sin embargo, puede hacer una protección de tipo para ayudar al compilador a comprender que las matrices son del tipo number[] en lugar de (number | null)[] . Esto también ayudará con la refactorización un poco.
Tenga en cuenta que también hay un problema con su lógica de verificación de tipo actual, pero parece que es solo un error tipográfico: el !! antes de que la primera condición debería ser solo una ! .
Aquí hay un protector de tipo simple:
function isNonNullNumberArray(x: (number | null)[]): x is number[] { return x.every(e => e !== null); } Entonces puedes usar isNonNullNumberArray así:
if (!(isNonNullNumberArray(clientRoasArray) && isNonNullNumberArray(roasArray))) { //if one of the arrays has null values don't calculate return 'no data'; } // now the compiler knows that neither array contains null values if (clientRoasArray.length === roasArray.length) { const discArray = clientRoasArray.map((value: number, index: number) => value - roasArray[index]); return calcAvg(discArray); } return 'no data'; También podría hacer una protección de tipo más general como esta si quisiera usar genéricos para que funcione para tipos distintos de number :
function isNonNullArray<T>(x: T[]): x is Exclude<T, null>[] { return x.every(e => e !== null); } Por cierto, no es necesario que declare explícitamente los tipos de parámetros en las funciones que se pasan al map y otros métodos de matriz, ya que el compilador puede inferir los tipos en función de la firma del método de matriz (por ejemplo, map ).
¡Puede verificar si su variable no es null y usar ! (operador de aserción no nulo) para decirle a TypeScript que la expresión no puede ser null . También el tipo de value es number | null
if (clientRoasArray.length === roasArray.length) { const discArray = clientRoasArray.map((value: number | null, index: number) => { return value && roasArray[index] ? value - roasArray[index]! : null }); return calcAvg(discArray as number[]); }