Skip to content

exceljs 4.4.0 ships its own module-local Buffer interface, so no Node Buffer can be passed to Workbook.xlsx.load — 6 sites pay it with as any, the 7th is a ledgered error #13378

Description

@os-trump

Filed unassigned and ungraded by the domain:cli dev seat, session session_01TvqBFLRzXdSPcbusDoED9k, while paying down packages/rest's test-typecheck ledger under #12573. Not graded, not routed.

The defect, in the dependency's own bytes

exceljs@4.4.0's index.d.ts opens with:

declareinterfaceBufferextendsArrayBuffer{}

That file carries 106 top-level export declarations, so it is a module — which means this Buffer is module-local and shadows Node's global Buffer inside every exceljs signature. The one that matters here is at index.d.ts:1490:

load(buffer: Buffer,options?: Partial<XlsxReadOptions>): Promise<Workbook>;

Workbook.xlsx.load does not ask for a Node Buffer. It asks for something structurally identical to ArrayBuffer. A Node Buffer is a Uint8Array, so it is not assignable, and tsc says so precisely:

src/rest.test.ts(1267,26): error TS2345: Argument of type 'Buffer<ArrayBuffer>' is not assignable to parameter of type 'Buffer'.
The types of 'slice(...)[Symbol.toStringTag]' are incompatible between these types.
Type '"Uint8Array"' is not assignable to type '"ArrayBuffer"'.

⭐ There is no Node Buffer value that satisfies this parameter. The defect is in the published declaration, not at any call site.

What the repo does about it today

8 xlsx.load( call sites in packages/rest/src:

  • 6 pay it with as anysrc/export-integration.test.ts (5 sites) and src/export-business-timezone.test.ts:242, all spelled wb.xlsx.load(getBuffer() as any);
  • 1 dodges it accidentally — src/import-prepare.ts:143, where the dynamic import is annotated any, so nothing is checked at all;
  • 1 does neither and is therefore a real, ledgered type errorsrc/rest.test.ts(1267,26), one of the entries in packages/rest/test-typecheck-debt.json.

⚠️ So the package's own established green form for this call is an any assertion, at 6 sites. #12573 forbids any for its paydown, which leaves that one site unrepairable within that card's fences — it is the reason src/rest.test.ts cannot reach zero.

The options, none of which is a one-line rider

  • A — a typed loader helper in the package's test utilities: one function that performs the assertion once and hands back a Workbook, with the 7 sites migrated onto it. Confines the dependency's defect to a single named place instead of 6 anonymous as anys.
  • B — a declaration override: exceljs's Buffer is module-local, so it cannot be reached by interface augmentation from outside; an override would have to redeclare the module, which replaces the package's whole typing surface. ⛔ Almost certainly worse than A.
  • C — upstream: check whether a later exceljs line drops the shim, and pin to it. Cheapest if it exists; nothing here has verified that it does.

⭐ A is the same shape as the answer this repo reaches for elsewhere — state the awkward requirement once, in a named place, rather than at every call site.

Re-check

sed -n '1p;1490p' node_modules/.pnpm/exceljs@4.4.0/node_modules/exceljs/index.d.ts
grep -rn "xlsx.load(" --include=*.ts packages/rest/src
cd packages/rest && npx tsc --noEmit --pretty false -p tsconfig.test.json

Positive control for the grep: it reports 8 sites, so a zero would read as an instrument failure rather than a finding.

Refs

Severity not judged; no urgency claimed — this is a compile-time typing defect with no runtime component, and the ratchet pins it either way.

Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions