cloudflare / workerd

The JavaScript / Wasm runtime that powers Cloudflare Workers
https://blog.cloudflare.com/workerd-open-source-workers-runtime/
Apache License 2.0
6.14k stars 291 forks source link

Standard `WebSocket` type returned instead of type from `@cloudflare/workers-types` #1775

Closed missinglink closed 2 months ago

missinglink commented 7 months ago

Version

"@cloudflare/workers-types": "4.20240222.0"

Expected

deserializeAttachment() exists on WebSocket type returned by DurableState.getWebSockets()

Screenshot 2024-03-07 at 11 43 29

Actual

DurableState.getWebSockets() returns type globalThis.WebSocket (standard type as defined by MDN Reference):

Screenshot 2024-03-07 at 11 39 50

Suggestion

The types file does in fact include a type for the Cloudflare implementation of WebSocket which includes this signature, but this is not the Type which is returned.

Screenshot 2024-03-07 at 11 40 16

Anecdote

Could this be due to the WebSocket type definition coming later in the types file than the DurableObjectState definition?

missinglink commented 7 months ago

Workaround:

declare module '@cloudflare/workers-types/experimental' {
  export interface DurableObjectState extends OriginalDurableObjectState {
    acceptWebSocket: (ws: WebSocket, tags?: string[]) => void
    getWebSockets: (tag?: string) => WebSocket[]
    getWebSocketAutoResponseTimestamp: (ws: WebSocket) => Date | null
    getTags: (ws: WebSocket) => string[]
  }
  export declare const WebSocketPair: {
    new (): {
      0: WebSocket
      1: WebSocket
    }
  }
}
russelldavis commented 6 months ago

I ran into this as well. I don't think it's actually a cloudflare issue though. The issue (at least for me) was that lib.dom.d.ts (which has the conflicting definition of WebSocket) was being inadvertently added to my project[1]. Even if it's not included in your tsconfig, if you have any dependencies that reference it, it gets pulled in globally (see https://github.com/microsoft/TypeScript/issues/43990 and https://github.com/microsoft/TypeScript/issues/37053)

[1] It was being added via fetch-retry package (which incidentally just fixed their types to stop doing that: https://github.com/jonbern/fetch-retry/pull/88).

missinglink commented 6 months ago

@russelldavis I still believe it's a CloudFlare issue as the second screenshot from within CloudFlare type definitions file shows a modal which lists the return type as globalThis.WebSocket.

I will investigate what you said and report back.

russelldavis commented 6 months ago

CloudFlare type definitions file shows a modal which lists the return type as globalThis.WebSocket

That's correct and expected. The Cloudflare library defines WebSocket as a global definition (because it is available without importing, per standards). Everything works properly as long as you don't import a library with a conflicting global WebSocket definition.

sionicion commented 5 months ago

I ran into this issue a couple months ago when a package in my project was using the WebSocket type from lib.dom.d.ts, the solution for me was to add the following declaration extending the WebSocket type with the @cloudflare/workers-types WebSocket type.

import { type WebSocket as CloudflareWebSocket } from '@cloudflare/workers-types'

declare global {
    interface WebSocket extends CloudflareWebSocket {}
  }
penalosa commented 2 months ago

@russelldavis is correct here—this is likely because lib.dom.d.ts has somehow been included in your project (often this is because a dependency includes /// <reference lib="dom" />).

@cloudflare/workers-types is not necessarily compatible with lib.dom.d.ts, and so we don't recommend using the two together. If you absolutely have to, then you can import the Cloudflare-specific types directly from @cloudflare/workers-types (although note these can only be used as type imports, and so e.g. constructing a WebSocket imported from @cloudflare/workers-types wouldn't work)