【问题标题】:Typescript interface for array of anything but other arrays用于除其他数组之外的任何数组的 Typescript 接口
【发布时间】:2021-11-06 15:47:38
【问题描述】:

flatten 接受任何数据类型的数组并生成一个数组,其中每个嵌套数组都展平。

例如,[{}, 'hello', 2, [3, ['ay'], 'oi oi'] 变为 [{}, 'hello', 2, 3, 'ay', 'oi oi'][[[[5]]]] 变为 [5]

我需要一个接口来描述这样的功能。我开始写它时认为它会很简单,但后来我坚持描述一个除了其他数组之外的任何东西的数组。

interface Flatten {
  (any[]): ...?
}

欢迎任何想法,谢谢:))

【问题讨论】:

    标签: typescript tuples variadic-tuple-types


    【解决方案1】:

    它在 TS 4.5(夜间版本)中是可行的,但不是您所期望的。

    感谢variadic-tuple-types,您可以这样做:

    
    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)
    

    Playground

    您还应该向reduce 方法添加第二个参数。

    更多解释你可以在我的article找到。

    如果您想更好地了解 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])
    }
    

    【讨论】:

    • 这对我来说看起来真的很复杂。如何将其用作函数返回值类型?
    • 我稍后会做更多解释
    • Result 不工作
    • @PYTHONDEVELOPER999 请查看更新后的代码
    【解决方案2】:

    如果你知道平面数组应该返回的所有数据类型,你可以像下面这样定义它:

    type Flat = string|number|FlatObj;
    type FlatArr = Array<Flat>;
    type FlatObj = {[key: string]: Flat|FlatArr};
    

    在您的示例中,您有嵌套的空对象,所以我假设您也希望它只包含平面数组,FlatObj 是它的定义。

    现在,如果您尝试在任何 FlatArr 变量中分配嵌套数组,它应该会报错。

    链接到playground

    如果您正在寻找 Type Guard,这里有一个 FlatArrFlatObj 的示例:

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

    【讨论】:

    • 它不工作。当我简单地返回相同的数组时,打字稿不会警告我
    • 也许将FlatArray 重命名为FlatArr,因为FlatArray 可能在某处定义。我使用此更改测试了代码,当变量类型为 FlatArr 时,TypeScript (4.4.2) 确实警告我任何嵌套数组,这是上面示例中 flatten 函数的返回类型。
    • 检查this。 TS 不需要类型保护。
    猜你喜欢
    • 2019-02-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 2012-09-15
    • 1970-01-01
    • 2018-02-21
    相关资源
    最近更新 更多