【问题标题】:TypeScript: Generics type definition for TypedArrayTypeScript:TypedArray 的泛型类型定义
【发布时间】:2017-05-15 20:30:32
【问题描述】:

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

例如,当你通过new Uint32Array([1,2,3]),新大小为5,它会返回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 定义接口,但单个类型化数组似乎没有从公共类继承...例如,我可以使用 const expand = (source: &lt;Uint32Array|Uint16Array|...&gt;): &lt;Uint32Array|Uint16Array|...&gt; =&gt; {},而不是使用泛型。但它失去了返回类型与源数组相同类型的上下文。

对于 2) 我对如何解决此错误一无所知。 TSC 抱怨源的构造函数缺少类型信息似乎是合理的。但是如果我可以为 1) 传递正确的类型,我认为 2) 也会消失。

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

【问题讨论】:

    标签: javascript generics typescript typed-arrays


    【解决方案1】:

    这并不漂亮,但它确实有效:

    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,我将使用您的实现并考虑 @tamas-hegedus 的想法。我认为这个用例可以接受类型转换,因为单元测试可以很容易地覆盖极端情况。
    【解决方案2】:

    我抓住了@Nitzan 的答案并对其进行了按摩,直到所有类型转换都消失了,但不幸的是,这个解决方案受到untyped constructor property issue here 的影响,而且似乎没有没有类型转换的解决方法。无论如何我都会发布代码以供将来参考。

    警告:此代码自 2017/05/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); 
    

    【讨论】:

      【解决方案3】:

      您可以简单地在声明文件中定义一个通用的TypedArray

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

      2- 添加以下行:

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

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

      【讨论】:

        猜你喜欢
        • 2012-02-04
        • 1970-01-01
        • 2019-07-21
        • 2012-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-26
        • 1970-01-01
        相关资源
        最近更新 更多