typescript 泛型函数中的类型化数组

zynd9foi  于 2022-12-24  发布在  TypeScript
关注(0)|答案(1)|浏览(355)

在一个库中,我有一个helper函数,它可以处理任何类型的数组,现在我想扩展它来接受typed arrays,但是我找不到一个公共基类,类型化数组也不是泛型的,这是我目前为止为泛型数组和一个类型化数组编写的代码:

public static copyOf(original: Int32Array, newLength: number): Int32Array;
    public static copyOf<T>(original: T[], newLength: number): T[];
    public static copyOf<T>(original: T[] | Int32Array, newLength: number): T[] | Int32Array {
        if (original instanceof Int32Array) {
            if (newLength < original.length) {
                return original.slice(0, newLength);
            }

            if (newLength === original.length) {
                return original.slice();
            }

            const result = new Int32Array(newLength);
            result.set(original);

            return result;

        } else {
            if (newLength < original.length) {
                return original.slice(0, newLength);
            }

            if (newLength === original.length) {
                return original.slice();
            }

            const result = new Array<T>(newLength);
            result.splice(0, 0, ...original);

            return result;
        }
    }

然而,这种方法需要为每个类型化数组类型添加一个自己的分支,这将挫败泛型的思想。如何以更紧凑的方式实现这一点?

blmhpbnm

blmhpbnm1#

首先是类型,因为这是TypeScript。目前没有一个内置类型对应于类型化数组;请参见microsoft/TypeScript#15402。但是没有什么可以阻止您将自己的类型定义为相关单个类型的联合。下面是一种可能性:

type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array |
  Uint32Array | Uint8ClampedArray | Float32Array | Float64Array;

我没有在这里包括BigInt64ArrayBigUint64Array,因为它们的行为与其他的不完全相同(您需要bigint而不是number),但是如果需要,您可以添加它们。
现在来看看实现。一种方法是使用传入的类型化数组的constructor property来获取正确的类构造函数,并使用它来构造一个新示例。TypeScript不会乐意这样做。因为它不对强类型constructor属性建模(参见ms/TS#3841中关于这个的详细讨论)......所以我们需要使用类型Assert来说服编译器它是可接受的。
可能是这样的:

class Foo {
  public static copyOf<T extends TypedArray>(original: T, newLength: number): T;
  public static copyOf<T>(original: T[], newLength: number): T[];
  public static copyOf<T>(original: T[] | TypedArray, newLength: number): T[] | TypedArray {
    if (!Array.isArray(original)) {
      if (newLength < original.length) {
        return original.slice(0, newLength);
      }
      if (newLength === original.length) {
        return original.slice();
      }
      const result = new (original.constructor as new (arg: number) => TypedArray)(newLength);
      result.set(original);
      return result;
    } else {
      if (newLength < original.length) {
        return original.slice(0, newLength);
      }
      if (newLength === original.length) {
        return original.slice();
      }
      const result = new Array<T>(newLength);
      original.forEach((v, i) => result[i]=v); // splice doesn't do what you want
      return result;
    }
  }
}

这里我没有检查instanceof来检查值是否是特定类型的数组,而是使用the Array.isArray() method来检查值 * 不是 * 内置数组。
(Also注意,我不得不改变the splice() method,它 * insert * things,导致结果数组比您预期的要长。)
如果需要支持BigInt64ArrayBigUint64Array,那么必须在result.set(original);行添加类型Assert以防止错误。
让我们确保它的工作:

const arr = Foo.copyOf(new Int16Array([1, 2, 3, 4, 5]), 10);
// const arr: Int16Array
console.log(arr) // Int16Array: ...

const arr2 = Foo.copyOf(["a", "b", "c"], 4);
// const arr2: string[]
console.log(arr2) // ["a", "b", "c", ]

看起来不错。
Playground代码链接

相关问题