我是TypeScript的新手,刚刚开始将我的应用程序从JavaScript迁移到TypeScript。
- 第一个函数:
calcAvg
,取一个数字数组并返回它们的平均值 - 第二个函数:
avgRoasDiscrepancy
,取两个数组,如果这两个数组长度相等且没有空值,它将从一个列表中减去第二个数组中具有相同索引的元素的每个元素
问题出现在以下map
函数中(请参阅下面的完整代码(:
const discArray = clientRoasArray.map((value: number, index: number) => value - roasArray[index]);
我得到以下错误:
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'
我不知道如何";说服";TypeScript,value
和roasArray[index]
实际上都是数字,而不是null。
这是完整的代码:
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';
};
在行中
const clientRoasArray = advertiserStats.map((obj: DailyData) => obj.clientCost > 0 ? obj.clientConvValue/obj.clientCost: null);
您假设clientRoasArray的元素可以是number
或null
,这在您的条件: null
中是清楚的
在这一行中,您将clientRoasArray
的值标识为number only
,这是错误的,因为您告诉代码该值可能是number|null
你只需要更改这条线路
const discArray = clientRoasArray.map((value: number, index: number) => value - roasArray[index]);
到这个
const discArray = clientRoasArray.map((value: number | null, index: number) => value - roasArray[index]);
但你将面临roasArray[index]
的问题,因为它也可能是null
您可以检查变量是否不是null
,并使用!
(非null断言运算符(告诉TypeScript表达式不能是null
。此外,CCD_ 17的类型是CCD_ 18。
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[]);
}
使用对数组的every
调用,我无法使类型推断正常工作,但是您可以进行类型保护,以帮助编译器了解数组的类型是number[]
而不是(number | null)[]
。这也将有助于重构。
请注意,当前的类型检查逻辑也有问题,但看起来只是一个拼写错误:第一个条件之前的!!
应该只是一个!
。
这里有一个简单的防护装置:
function isNonNullNumberArray(x: (number | null)[]): x is number[] {
return x.every(e => e !== null);
}
然后你可以像这样使用isNonNullNumberArray
:
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';
如果你想使用泛型,你也可以制作一个更通用的类型保护,这样它就可以用于number
:以外的类型
function isNonNullArray<T>(x: T[]): x is Exclude<T, null>[] {
return x.every(e => e !== null);
}
顺便说一句,您不需要在传递给map
和其他数组方法的函数中显式声明参数类型,因为编译器可以根据数组方法的签名推断类型(例如map
(。