Search code examples
typescriptkeepasstypescript-5

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.


Solution

  • 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')));
        // ...
    }