@cloudflare/workers-types declares Buffer and process as const, which discards the @types/node globals in any project that uses both.
index.d.ts (lines 486–487 in 5.20260817.1):
declare const Buffer: any;
declare const process: any;
@types/node declares var Buffer: BufferConstructor inside declare global { … } in buffer.buffer.d.ts. A var merges across declaration files; a block-scoped const does not. The redeclaration discards the @types/node global block, and ordinary Node code stops type-checking.
Affected: any project listing both @cloudflare/workers-types and node in compilerOptions.types.
|
|
| First affected version |
5.20260807.2 |
| Last clean version |
5.20260804.1 |
| Still present in |
5.20260817.1 (current latest) |
Bisected by unpacking each published tarball and grepping index.d.ts, so the boundary is exact rather than inferred from a changelog. Ten consecutive releases now carry it.
Reproduction
package.json — @cloudflare/workers-types@5.20260817.1, @types/node@22.20.1, typescript@5.9.3.
tsconfig.json:
{
"compilerOptions": {
"types": ["node", "@cloudflare/workers-types"],
"strict": true,
"skipLibCheck": true,
"noEmit": true
},
"include": ["src"]
}
src/index.ts:
import { randomBytes } from "node:crypto";
export const id = (): string => randomBytes(8).toString("hex");
tsc --noEmit:
src/index.ts(2,50): error TS2554: Expected 0 arguments, but got 1.
The real error, which skipLibCheck hides
With skipLibCheck: false TypeScript states the actual problem:
node_modules/@cloudflare/workers-types/index.d.ts(486,15): error TS2451: Cannot redeclare block-scoped variable 'Buffer'.
node_modules/@types/node/buffer.buffer.d.ts(356,19): error TS2451: Cannot redeclare block-scoped variable 'Buffer'.
skipLibCheck: true — the default in most templates, including Cloudflare's own — suppresses the TS2451 and leaves only the downstream TS2554. That is why this presents as a mystery about randomBytes rather than as a redeclaration. A type probe shows randomBytes(8).toString resolving to () => string, i.e. Uint8Array.prototype.toString, because the Buffer interface members in the discarded global block are gone.
Isolated
Each row run, not reasoned about:
| change |
result |
types order reversed |
still fails — ordering is irrelevant |
types: ["node"] alone |
clean |
pin 5.20260804.1 |
clean |
declare const → declare var on both lines |
clean |
| delete both lines |
clean |
delete the process line only |
still fails — Buffer alone is the cause |
delete the Buffer line only |
clean |
process has the same collision, and it is silent
declare const process: any collides with @types/node's var process identically. It produces no error only because any absorbs every member access — so rather than failing, it silently degrades process from NodeJS.Process to any. Anyone with both type sets has lost that typing without being told.
Suggested fix
Declare them as var, which merges with @types/node rather than colliding with it:
declare var Buffer: any;
declare var process: any;
Or do not emit them at all — a Workers project that wants Buffer or process types installs @types/node and sets nodejs_compat, and gets the real declarations rather than any.
Prior art
cloudflare/workerd#1298 — "Transitive loading of @types/node breaks Request/Response (etc.) types" — is the same collision class pointing the other way: @types/node clobbering workers-types when Node began declaring its own Request/Response. This is the mirror image.
Found while pinning dependency floors ahead of a first release of a Workers framework built on workerd. Happy to open a PR with the const → var change if that is the preferred fix.
@cloudflare/workers-typesdeclaresBufferandprocessasconst, which discards the@types/nodeglobals in any project that uses both.index.d.ts(lines 486–487 in5.20260817.1):@types/nodedeclaresvar Buffer: BufferConstructorinsidedeclare global { … }inbuffer.buffer.d.ts. Avarmerges across declaration files; a block-scopedconstdoes not. The redeclaration discards the@types/nodeglobal block, and ordinary Node code stops type-checking.Affected: any project listing both
@cloudflare/workers-typesandnodeincompilerOptions.types.5.20260807.25.20260804.15.20260817.1(currentlatest)Bisected by unpacking each published tarball and grepping
index.d.ts, so the boundary is exact rather than inferred from a changelog. Ten consecutive releases now carry it.Reproduction
package.json—@cloudflare/workers-types@5.20260817.1,@types/node@22.20.1,typescript@5.9.3.tsconfig.json:{ "compilerOptions": { "types": ["node", "@cloudflare/workers-types"], "strict": true, "skipLibCheck": true, "noEmit": true }, "include": ["src"] }src/index.ts:tsc --noEmit:The real error, which
skipLibCheckhidesWith
skipLibCheck: falseTypeScript states the actual problem:skipLibCheck: true— the default in most templates, including Cloudflare's own — suppresses theTS2451and leaves only the downstreamTS2554. That is why this presents as a mystery aboutrandomBytesrather than as a redeclaration. A type probe showsrandomBytes(8).toStringresolving to() => string, i.e.Uint8Array.prototype.toString, because theBufferinterface members in the discarded global block are gone.Isolated
Each row run, not reasoned about:
typesorder reversedtypes: ["node"]alone5.20260804.1declare const→declare varon both linesprocessline onlyBufferalone is the causeBufferline onlyprocesshas the same collision, and it is silentdeclare const process: anycollides with@types/node'svar processidentically. It produces no error only becauseanyabsorbs every member access — so rather than failing, it silently degradesprocessfromNodeJS.Processtoany. Anyone with both type sets has lost that typing without being told.Suggested fix
Declare them as
var, which merges with@types/noderather than colliding with it:Or do not emit them at all — a Workers project that wants
Bufferorprocesstypes installs@types/nodeand setsnodejs_compat, and gets the real declarations rather thanany.Prior art
cloudflare/workerd#1298— "Transitive loading of@types/nodebreaks Request/Response (etc.) types" — is the same collision class pointing the other way:@types/nodeclobberingworkers-typeswhen Node began declaring its ownRequest/Response. This is the mirror image.Found while pinning dependency floors ahead of a first release of a Workers framework built on workerd. Happy to open a PR with the
const→varchange if that is the preferred fix.