递归数组类型.map在Typescript中



我有一个简单的例子,将嵌套的字符串数组转换为嵌套的数字数组,没有扁平

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));

最新更新