JS 到 TS:类型 'null' 不可分配给类型 'number'
JS to TS: Type 'null' is not assignable to type 'number'
我是 TypeScript 的新手,刚开始将我的应用程序从 JavaScript 迁移到 TypeScript。
- 第一个函数:
calcAvg
,接受一组数字和 returns 它们的平均值。
- 第二个函数:
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]
实际上都是数字而不是空值。
完整代码如下:
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
我无法使用对数组的 every
调用使类型推断正常工作,但是您可以创建一个 type guard 来帮助编译器理解数组的类型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
).
您可以检查您的变量是否不是 null
并使用 !
(non-null 断言运算符)告诉 TypeScript 表达式不能是 null
。 value
的类型也是 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[]);
}
我是 TypeScript 的新手,刚开始将我的应用程序从 JavaScript 迁移到 TypeScript。
- 第一个函数:
calcAvg
,接受一组数字和 returns 它们的平均值。 - 第二个函数:
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]
实际上都是数字而不是空值。
完整代码如下:
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
我无法使用对数组的 every
调用使类型推断正常工作,但是您可以创建一个 type guard 来帮助编译器理解数组的类型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
).
您可以检查您的变量是否不是 null
并使用 !
(non-null 断言运算符)告诉 TypeScript 表达式不能是 null
。 value
的类型也是 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[]);
}