Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert "Buffer<ArrayBufferLike>" to "ArrayBuffer" in TypeScript 5.7?

According to the TypeScript 5.7 announcement, I am now affected by the ES2024 change for Buffers. Especially this change of Buffer Generics.

I am working with a KeePass file. For that I load the file and open it via npm package "kdbxweb" "^2.1.1".
Context: "typescript": "^5.7.3", "@types/node": "^22.10.5". (i.e. currently latest versions)

import fs from 'fs';
import path from 'path';
import * as kdbxweb from 'kdbxweb';

async getClientSecret(): Promise<string> {

    const fileBuffer: Buffer<ArrayBufferLike> = fs.readFileSync(KeePassFilePath);
    const keePassFileBuffer: ArrayBufferLike = fileBuffer.buffer;
    const keePassDB: kdbxweb.Kdbx = await kdbxweb.Kdbx.load(keePassFileBuffer, new kdbxweb.Credentials(kdbxweb.ProtectedValue.fromString(myPW)));
    // ...
}

After the upgrade to "ES2024" (via "ESNext"), npx tsc complains:

error TS2345: Argument of type 'ArrayBufferLike' is not assignable to parameter of type 'ArrayBuffer'.
      Type 'SharedArrayBuffer' is missing the following properties from type 'ArrayBuffer': resizable, resize, detached, transfer, transferToFixedLength

I don't know how to solve this. The library's load function is declared as:

static load(data: ArrayBuffer, credentials: KdbxCredentials, options?: {
        preserveXml?: boolean;
    }): Promise<Kdbx>;

While fs.readFileSync() returns a Buffer<ArrayBufferLike> (coming from "@types/node": "^22.10.5"), which makes the buffer property return an ArrayBufferLike.

How can I bring them together (without expecting the TS error or casting to unknown etc.)? I.e. how to convert from Buffer<ArrayBufferLike> to ArrayBuffer properly?


Blindly casting to

const fileBuffer = fs.readFileSync(KeePassFilePath) as Buffer<ArrayBuffer>;

does silence the error, but how am I supposed to know the correct actual type? Imagine other suppliers, which don't give some kind of guarantee about this being an actual ArrayBuffer. So this is not a viable solution to me.

like image 348
Vankog Avatar asked Jun 21 '26 13:06

Vankog


2 Answers

As ArrayBufferLike can be at least either an ArrayBuffer | Shared ArrayBuffer | ..., I found that we can wrap the Buffer<ArrayBufferLike> into an Uint8Array to guarantee an ArrayBuffer:

import fs from 'fs';
import path from 'path';
import * as kdbxweb from 'kdbxweb';

async getClientSecret(): Promise<string> {

    const fileBuffer: Buffer<ArrayBufferLike> = fs.readFileSync(KeePassFilePath);
    const keePassFileBuffer: ArrayBuffer = new Uint8Array(fileBuffer).buffer;
    const keePassDB: kdbxweb.Kdbx = await kdbxweb.Kdbx.load(keePassFileBuffer, new kdbxweb.Credentials(kdbxweb.ProtectedValue.fromString('123')));
    // ...
}
like image 77
Vankog Avatar answered Jun 25 '26 17:06

Vankog


The following seems to work and satisfy the type checker, but it's probably not be the most efficient way of going about it as it involves the creation of a few Uint8Arrays.

const fileBuffer: ArrayBufferLike = new SharedArrayBuffer(16);
const ui8Array: Uint8Array<ArrayBufferLike> = new Uint8Array(fileBuffer);
const actualArrayBuffer: ArrayBuffer = new ArrayBuffer(ui8Array.length);
const actualArrayBufferView: Uint8Array<ArrayBuffer> = new Uint8Array(actualArrayBuffer);
actualArrayBufferView.set(ui8Array);

// actualArrayBuffer now holds the contents of fileBuffer, but is of type ArrayBuffer
like image 25
abbm Avatar answered Jun 25 '26 15:06

abbm