我有一个简单的例子,将嵌套的字符串数组转换为嵌套的数字数组,没有扁平
const strArr = ["1", "2", ["3", "4"], "5"];
function mapToNumber(item: string | Array<string>): number | Array<number> {
if (Array.isArray(item)) {
return item.map(mapToNumber); // line Hey
} else {
return Number(item);
}
}
console.log(strArr.map(mapToNumber));
然而TS对我大喊:Type '(number | number[])[]' is not assignable to type 'number | number[]'
。然后我把Hey
换成return item.map<number>(mapToNumber)
,不行。我想到了函数重载,我试了一下:
const isStringArray = (arr: any): arr is Array<string> => {
return Array.isArray(arr) && arr.every(item => typeof item === "string");
}
function mapToNumber(item: string): number;
function mapToNumber(item: Array<string>): Array<number>;
function mapToNumber(item: any) {
if (isStringArray(item)) {
return item.map<number>(mapToNumber);
} else {
return Number(item);
}
}
console.log(strArr.map(mapToNumber));
即使我添加了自定义类型保护,仍然不起作用。
逻辑很简单,但是我如何为这个简单的情况定义正确的类型呢?游乐场链接
编辑:
我给了泛型一个尝试,仍然不工作
function mapToNumber3<T extends string | Array<string>>(item: T): T extends string ? number : Array<number> {
if (Array.isArray(item)) {
return item.map(mapToNumber3);
} else {
return Number(item);
}
}
为了做到这一点,你可以使用f有界量化:
const strArr = ["1", "2", ["3", "4"], "5"];
const mapToNumber = (item: string) => parseInt(item, 10)
const isString = (item: unknown): item is string => typeof item === "string"
const isStringArray = (arr: any): arr is Array<string> => Array.isArray(arr) && arr.every(isString)
const map = <
N extends number,
Elem extends `${N}` | Array<Elem>,
T extends Array<T | Elem>,
>(arr: [...T]): Array<unknown> => {
return arr.map((item) => {
if (isStringArray(item)) {
return item.map(mapToNumber);
}
if (Array.isArray(item)) {
return map(item)
}
if (isString(item)) {
return mapToNumber(item)
}
return item
})
}
const result = map(["1", "2", ["3", "4", ['6', ['7']]], "5"])
T extends Array<T | Elem>
- T是递归泛型
最安全的方法是返回Array<unknown>
,因为你不知道数组的深度操场上
在typescript中创建递归数据结构类型相对容易。见这里,但是很难将它用作函数
中的返回类型。我会尝试只是定义一个类型:type NestedArray<T> = T | NestedArray<T>[]
表示类型为T的嵌套数组。然后,您只需在原始函数中键入变量,它就可以工作了。
type NestedArray<T> = T | NestedArray<T>[]
const strArr: NestedAr
ray<string> = ["1", "2", ["3", "4"], "5"];
const isStringArray = (arr: any): arr is Array<string> => {
return Array.isArray(arr) && arr.every(item => typeof item === "string");
}
function mapToNumber(item: NestedArray<string>): NestedArray<number> {
if (isStringArray(item)) {
return item.map(mapToNumber);
} else {
return Number(item);
}
}
console.log(strArr.map(mapToNumber));
关于这个Github问题的更多信息(基本上,目前没有很好的方法来做到这一点)。你在这里想要什么建议
我认为你现在只需要使用as
:
const strArr = ["1", "2", ["3", "4"], "5"];
function mapToNumber(item: string | string[]): number | number[] {
if (typeof item === 'string') {
return Number(item);
} else {
return item.map(mapToNumber) as number[]; // Here
}
}
console.log(strArr.map(item => mapToNumber(item)));
如果您再添加一个重载,那么您的重载示例从技术上讲是有效的。你看你觉得值不值得。
const strArr = ["1", "2", ["3", "4"], "5"];
const isStringArray = (arr: any): arr is Array<string> => {
return Array.isArray(arr) && arr.every(item => typeof item === "string");
}
function mapToNumber(item: string): number;
function mapToNumber(item: string[]): number[];
function mapToNumber(item: string | string[]): number | number[]; // Add this
function mapToNumber(item: any) {
if (isStringArray(item)) {
return item.map<number>(mapToNumber);
} else {
return Number(item);
}
}
console.log(strArr.map(mapToNumber));