第一个参数必须是字符串类型或 Buffer、ArrayBuffer 或 Array 的实例。收到的类型编号 - 使用 Uint8Array.slice() 时

The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array. Received type number - When using Uint8Array.slice()

我的代码实现是 Uint8Array 的以下扩展 class。

export class ByteArray extends Uint8Array {
    ...

    private _encoded: string;

    ...

    constructor(_encoded: string) {
        super(Buffer.from(_encoded, "base64"));
        this._encoded = _encoded;
    }
}

我收到错误:

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type number (134)

每当我尝试使用 this.slice(start, end)。例如 this.slice(4, 10);。非常感谢任何帮助。

问题是由您的构造函数以及如何通过 Uint8Array.slice() 的实现调用它引起的。该 .slice() 方法的定义表明它将创建一个新的 TypedArray 并在其下创建一个新的 Buffer 并为此调用调用 .slice() 的对象的构造函数.那将是您的 ByteArray class 的构造函数,因为该对象是 ByteArray。因此,它会调用 ByteArray class.

的构造函数

但是,您的构造函数不支持 .slice() 尝试使用的 Uint8Array 构造函数的形式。 Uint8Array 构造函数支持所有这些形式:

new Uint8Array(); // new in ES2017
new Uint8Array(length);
new Uint8Array(typedArray);
new Uint8Array(object);

new Uint8Array(buffer);
new Uint8Array(buffer, byteOffset);
new Uint8Array(buffer, byteOffset, length);

可能 .slice() 正在使用最后一种形式,这会导致您的构造函数调用:

 super(Buffer.from(_encoded, "base64"));

但是,在这种情况下,_encoded 将是一个缓冲区对象,而不是 Buffer.from(_encoded, "base64") 期望的字符串,因此会出现错误。

有几种方法可以解决这个问题:

检测您的构造函数参数,让其他人通过

您可以通过将不仅仅是单个字符串的任何构造函数参数传递给常规构造函数来修复它。但是,这将创建一个没有设置 _encoded 属性 的 ByteArray class 对象。我不确定您真正想对 .slice() returns.

对象做什么

这是一个可以检测您的特定构造函数并以其他方式将内容传递给 Uint8Array 构造函数(正常 Javascript,而非 TypeScript)的版本:

class ByteArray extends Uint8Array {
    constructor(...args) {
        let _encoded = args[0];
        // see if constructor arguments are my string argument
        if (typeof _encoded === "string") {
            super(Buffer.from(_encoded, "base64"));
            this._encoded = _encoded;
        } else {
            // not my string, pass through all constructor arguments
            // to default constructor
            super(...args);
        }
    }
}

注意:当您 sub-class 一个包含创建其自身新版本的方法的对象时,这是一个普遍问题。这些方法将尝试使用现有对象类型的构造函数,然后调用该构造函数来创建新对象。您的构造函数必须支持现有形式的构造函数参数才能正常工作,因为您不知道这些其他方法可能使用哪种形式的构造函数参数。

告诉 Uint8Array 方法创建 Uint8Array 对象

解决此问题的另一种方法是指定您不希望 .slice() 或在 ByteArray 上调用的任何其他方法创建 ByteArray 对象,而是创建一个Uint8Array。你可以这样做:

class ByteArray extends Uint8Array {
    // when creating new objects from methods of this one,
    // make them regular Uint8Array objects, not ByteArray objects
    static get[Symbol.species]() { return Uint8Array; }

    constructor(_encoded) {
        super(Buffer.from(_encoded, "base64"));
    }
}

然后,当.slice()去创建一个新对象时,它会创建一个Uint8Array而不是你的ByteArray并且Uint8Array的构造函数将正常工作。这意味着在您的 ByteArray 对象之一上调用 .slice() 的结果将不是 ByteArray 对象——而是 Uint8Array

您可以在 MDN 上阅读有关 Symbol.species 属性 here 的信息。

一般性讨论

您 运行 遇到的问题是 sub-classing 任何对象的问题,这些对象的方法试图创建一个与调用对象相同 class 的新对象在。例如,sub-classing 一个 Array 对象将与 .slice() 有同样的问题。要支持所有这些基本 class 方法的全部功能,您必须支持基本 class 方法可能使用的所有构造函数参数,或者您必须使用 .species 支持来告诉那些基础 class 方法,您不希望它们创建您的 sub-class 的对象,而是创建 base-class 的对象(因此它们不会调用您的构造函数)。