TypeScript:TypedArray 的泛型类型定义

TypeScript: Generics type definition for TypedArray

我正在尝试编写一个函数,通过将任意 TypedArray 作为输入来扩展/缩小 TypedArray,returns 一个具有不同大小的新的相同类型的 TypedArray,并将原始元素复制到其中。

例如,当您通过 new Uint32Array([1,2,3]) 新尺寸 5 时,它将 return new Uint32Array([1,2,3,0,0]).

export const resize = <T>(
    source: ArrayLike<T>, newSize: number,
): ArrayLike<T> => {
    if (!source.length) { return new source.constructor(newSize); }
    newSize = typeof newSize === "number" ? newSize : source.length;
    if (newSize >= source.length) {
        const buf = new ArrayBuffer(newSize * source.BYTES_PER_ELEMENT);
        const arr = new source.constructor(buf);
        arr.set(source);
        return arr;
    }
    return source.slice(0, newSize);
};

虽然代码按预期工作,但 TSC 抱怨 1) ArrayType 没有 BYTES_PER_ELEMENTslice,以及 2) Cannot use 'new' with an expression whose type lacks a call or construct signature 语句 new source.constructor() .

有没有办法为 TSC 理解我的意图的此类函数指定类型接口?

对于 1),我理解 ArrayLike 没有为 TypedArray 定义的接口,但是单个类型化数组似乎没有继承自通用 class...例如,我可以使用而不是使用泛型const expand = (source: <Uint32Array|Uint16Array|...>): <Uint32Array|Uint16Array|...> => {}。但是它失去了 returning 类型与源数组相同类型的上下文。

对于 2) 我不知道如何解决这个错误。 TSC 抱怨 source 的构造函数缺少类型信息似乎是合理的。但是如果我可以为 1) 传递正确的类型,我想 2) 也会消失。

ref) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray

这不是很漂亮,但很管用:

type TypedArray = ArrayLike<any> & {
    BYTES_PER_ELEMENT: number;
    set(array: ArrayLike<number>, offset?: number): void;
    slice(start?: number, end?: number): TypedArray;
};
type TypedArrayConstructor<T> = {
    new (): T;
    new (size: number): T;
    new (buffer: ArrayBuffer): T;
    BYTES_PER_ELEMENT: number;
}

export const resize = <T extends TypedArray>(source: T, newSize: number): T => {
    if (!source.length) {
        return new (source.constructor as TypedArrayConstructor<T>)();
    }

    newSize = typeof newSize === "number" ? newSize : source.length;

    if (newSize >= source.length) {
        const buf = new ArrayBuffer(newSize * source.BYTES_PER_ELEMENT);
        const arr = new (source.constructor as TypedArrayConstructor<T>)(buf);
        arr.set(source);
        return arr;
    }
    return source.slice(0, newSize) as T;
};

(code in playground)

我抓住了@Nitzan 的回答并修改它直到所有类型转换都消失了,但不幸的是这个解决方案受到 untyped constructor property issue here 的影响,并且没有类型转换似乎没有解决方法。我post代码无论如何供将来参考。

警告:截至 2017 年 5 月 16 日,此代码无法编译。

interface GenericTypedArray<T> extends ArrayLike<number> {
    BYTES_PER_ELEMENT: number;
    set(array: ArrayLike<number>, offset?: number): void;
    slice(start?: number, end?: number): T;
    constructor: GenericTypedArrayConstructor<T>;
}

interface GenericTypedArrayConstructor<T> {
    new (): T;
    new (buffer: ArrayBuffer): T;
}

export function resize<T extends GenericTypedArray<T>>(source: T, newSize: number): T {
    if (!source.length) {
        return new source.constructor();
    }

    newSize = typeof newSize === "number" ? newSize : source.length;

    if (newSize >= source.length) {
        const buf = new ArrayBuffer(newSize * source.BYTES_PER_ELEMENT);
        const arr = new source.constructor(buf);
        arr.set(source);
        return arr;
    }
    return source.slice(0, newSize);
};

class DummyArray { 
    constructor();
    constructor(buffer: ArrayBuffer);
    constructor(array: ArrayLike<number>);
    constructor(arg?) { }

    // Hack to have a typed constructor property, see https://github.com/Microsoft/TypeScript/issues/3841
    'constructor': typeof DummyArray;
    BYTES_PER_ELEMENT: number;
    length: number;
    [index: number]: number;

    set(array: ArrayLike<number>, offset?: number): void { }
    slice(start?: number, end?: number): this { return this; }

    static BYTES_PER_ELEMENT: number;
}

// How it intended to work
resize(new DummyArray([1, 2, 3]), 5);

// How it fails to typecheck
// Types of property 'constructor' are incompatible.
//   Type 'Function' is not assignable to type 'GenericTypedArrayConstructor<Uint8Array>'.
resize(new Uint8Array([1, 2, 3]), 5); 

您可以简单地在声明文件中定义一个泛型 TypedArray

1- 在您的应用中创建一个名为 extras.d.ts 的文件。

2- 添加以下行:

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

然后通用 TypedArray 类型将在您的整个项目中可用。您可以使用此 extras.d.ts 文件继续声明更多自定义类型以在整个应用中使用。