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.
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')));
// ...
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With