flatten
接受任意数据类型的数组,并生成一个嵌套数组平坦化的数组。
例如,[{}, 'hello', 2, [3, ['ay'], 'oi oi']
变为[{}, 'hello', 2, 3, 'ay', 'oi oi']
,[[[[5]]]]
变为[5]
。
我需要一个接口来描述这样一个函数。我开始写它的时候认为它会很简单,但后来我坚持描述一个数组,除了其他数组。
interface Flatten {
(any[]): ...?
}
欢迎有任何想法,谢谢:))
在TS 4.5(夜间版本)中是可行的,但不是以您期望的方式。
多亏了可变元组类型,你可以这样做:
type Reducer<
Arr extends Array<unknown>,
Result extends Array<unknown> = []
> =
(Arr extends []
? Result
: (Arr extends [infer H, ...infer Tail]
? (H extends Array<any>
? Reducer<[...H, ...Tail], Result> : Reducer<Tail, [...Result, H]>) : never
)
)
// [1,2,3]
type Result = Reducer<[[[1], [[[[[[[2]]]]]]]], 3]>
// [1, 2, 3, 4, 5, 6]
type Result2 = Reducer<[[[[[[[[[1]]]]]]]],[[[[[[2,3,4]]]],[[[[5,6]]]]]]]>
我如何使用它作为函数返回值类型?
为了与函数一起使用,您需要将参数转换为不可变数组:
type Reducer<
Arr,
Result extends ReadonlyArray<unknown> = []
> = Arr extends ReadonlyArray<unknown> ?
(Arr extends readonly []
? Result
: (Arr extends readonly [infer H, ...infer Tail]
? (H extends ReadonlyArray<any>
? Reducer<readonly [...H, ...Tail], Result> : Reducer<Tail, readonly [...Result, H]>) : never
)
) : never
const flatten = <
Elem,
T extends ReadonlyArray<T | Elem>
>(arr: readonly [...T]): Reducer<T> =>
arr.reduce((acc, elem) =>
Array.isArray(elem)
? flatten(elem) as Reducer<T>
: [...acc, elem] as Reducer<T>,
[] as Reducer<T>
)
const result = flatten([[[[[[1]]], 2], 3]] as const)
游乐场
你还应该为reduce
方法添加第二个参数。
你可以在我的文章中找到更多的解释。
如果您想更好地理解Reducer
实用程序类型是如何工作的,请参见以下示例:
const Reducer = <T,>(Arr: ReadonlyArray<T>, Result: ReadonlyArray<T> = [])
: ReadonlyArray<T> => {
if (Arr.length === 0) {
return Result
}
const [Head, ...Tail] = Arr;
if (Array.isArray(Head)) {
return Reducer([...Head, ...Tail], Result)
}
return Reducer(Tail, [...Result, Head])
}
如果您知道flat array应该返回的所有数据类型,您可以像下面这样定义它:
type Flat = string|number|FlatObj;
type FlatArr = Array<Flat>;
type FlatObj = {[key: string]: Flat|FlatArr};
在您的示例中,您嵌套了空对象,所以我假设您希望它也只包含平面数组,FlatObj
是它的定义。
现在,如果你尝试在任何FlatArr
变量中分配嵌套数组,它应该会报错。
游乐场链接
如果你正在寻找类型保护,这里有一个FlatArr
和FlatObj
的例子:
type AnyObj = {[key: string]: any};
function isFlatObj(obj: AnyObj): obj is FlatObj {
let flat = true;
for (const key in obj) {
if (Array.isArray(obj[key]) && !isFlatArr(obj[key])) flat = false;
else if (typeof obj[key] === 'object' && !isFlatObj(obj[key])) flat = false;
}
return flat;
}
function isFlatArr(arr: any[]): arr is FlatArr {
let flat = true;
arr.map((item) => {
if (Array.isArray(item)) flat = false;
else if (typeof item === 'object' && !isFlatObj(item)) flat = false;
});
return flat;
}
要创建FlatArr,可以为它定义一个函数。我在下面做了两个函数。一个使数组变平,另一个确保对象数组也变平:
const flattenArr = (arr: any[]): FlatArr => {
const flatArr: FlatArr = [];
for (const item of arr) {
if (Array.isArray(item)) flatArr.push(...flattenArr(item));
else if (typeof item === 'string') flatArr.push(item);
else if (typeof item === 'number') flatArr.push(item);
else if (typeof item === 'object') flatArr.push(flattenObj(item));
}
return flatArr;
}
const flattenObj = (obj: AnyObj): FlatObj => {
let flatObj: FlatObj = {};
for (const key in obj) {
if (Array.isArray(obj[key])) flatObj[key] = flattenArr(obj[key]);
else if (typeof obj[key] === 'string') flatObj[key] = obj[key];
else if (typeof obj[key] === 'number') flatObj[key] = obj[key];
else if (typeof obj[key] === 'object') flatObj[key] = flattenObj(obj[key]);
}
return flatObj;
}