使用泛型制作 Array.flat(1) 的 Typescript 包装器

Posted

技术标签:

【中文标题】使用泛型制作 Array.flat(1) 的 Typescript 包装器【英文标题】:Making a Typescript wrapper of Array.flat(1) with generics 【发布时间】:2021-12-30 19:47:58 【问题描述】:

我正在尝试编写一个函数,该函数从参数创建一个数组,只接受一种类型或一个相同类型的深度数组。几乎是(...items)=>items.flat(1),但类型安全且使用泛型。我相当确定一个必要的限制是收集的类型不能是数组本身,因为.flat() 会将内容溢出到返回值中,从而污染它。

目标函数,但使用通用 T 而不是 number

function arrayOfNumber(...items: (number | number[])[]): number[] 
    return items.flat(1);

当直接用泛型替换number时,会失败:

function arrayOfGenericFailure<T>(...items: (T | T[])[]): T[] 
    return items.flat(1);

// Throws:
// Type '(T | (T extends readonly (infer InnerArr)[] ? InnerArr : T))[]' is not assignable to type 'T[]'.
//   Type 'T | (T extends readonly (infer InnerArr)[] ? InnerArr : T)' is not assignable to type 'T'.
//     'T' could be instantiated with an arbitrary type which could be unrelated to 'T | (T extends readonly (infer InnerArr)[] ? InnerArr : T)'.(2322)

我假设infer InnerArr 位用于解决前面提到的数组作为类型问题;如有错误请指正。

一个近在咫尺的解决方案:

type NotArray<T> = Exclude<T, Array<any>>;
export function arrayOfX<T>(...items: (NotArray<T> | NotArray<T>[])[]): T[] 
    return items.reduce((acc, val) => acc.concat(val), [] as T[]);

这通过了类型检查,但看起来很混乱,并且还限制了参数类型而不是泛型本身。有没有更好的方法来做到这一点?

【问题讨论】:

我看到您使用的是.flat(1)(顺便说一句,这是默认设置)。所以你不需要处理(比如说)T[][]? 没关系,你在arrayOfX 中的参数签名回答了这个问题,如果我正确看待它的话。 :-) 【参考方案1】:

两种选择:

一个简单的循环

由于您的数组元素类型明确地只是TT[],而flat 的类型明显复杂,因为它必须处理比这更深的事实,而且您似乎很乐意使用除此之外的其他东西flat,我会选择一个简单的for..of 循环:

function arrayOfX<ElementType>(a: (ElementType | ElementType[])[]): ElementType[] 
    const result: ElementType[] = [];
    for (const element of a) 
        if (Array.isArray(element)) 
            result.push(...element);
         else 
            result.push(element);
        
    
    return result;

这避免了任何类型断言或类型体操,并且非常易于阅读和维护。

Playground link

“软”类型断言

另一个答案——但我怀疑你考虑过并拒绝了这个——只是在flat 版本中抛出一个类型断言;类型足够相似,可以直接断言(不需要as unknown 子句):

function arrayOfX<ElementType>(a: (ElementType | ElementType[])[]): ElementType[] 
    return a.flat() as ElementType[];

Playground link

我在 small 这样的实用程序函数中尽可能严格地避免类型断言except,其中断言不可能不正确,只是我使用像 flat 这样的具有非常复杂类型的东西来处理我的实用程序函数不会抛出的情况。

【讨论】:

也就是说,如果像 Titian Cernicova-Dragomir 或 jcalz 这样的人可以想出一些类型体操来使 flat 版本在没有类型断言的情况下工作,我不会感到惊讶。

以上是关于使用泛型制作 Array.flat(1) 的 Typescript 包装器的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript Array flat 函数未在 React native 中定义

在PHP中展平数组

高大上的数组操作方法

13个不low的JS数组操作

泛型学习笔记

使用泛型来优化坐标类