From 29fa7bbe06508d56ae1c12dedfb945bddbcf86aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?chencheng=20=28=E4=BA=91=E8=B0=A6=29?= Date: Sat, 7 Nov 2026 16:23:16 +0800 Subject: [PATCH] chore(types): compile cleanly under the TypeScript 7 native compiler (#16463) * fix(types): compile cleanly under the TypeScript 7 native compiler tsgo (@typescript/native-preview) reported 48 errors across the typecheck projects while tsc 5.8 reported none. This removes all of them without changing what tsc emits, so a tsgo typecheck lane or a later compiler switch can start from a clean tree. - Drop `baseUrl` from five tsconfigs. TypeScript 7 removed it, and every `paths` target here is already relative to its tsconfig. - Set `rootDir` where TypeScript 7 no longer infers it (web-shell declarations, sdk-typescript), and list node types explicitly for cua-driver now that @types are no longer included by default. - Annotate Buffers built by Node's Buffer factories as `Buffer` (or widen to `Buffer` where a variable later receives stream chunks); the ES2024 lib no longer treats ArrayBufferLike as an ArrayBuffer, so these failed at fetch, Blob, and Response call sites. The two call sites that take caller- or interface-supplied bytes assert `BodyInit`, matching the existing XMLHttpRequest upload path. - Remove three redundant `?? []` / `?? undefined` operands that the newer checker reports as unreachable. - Declare `*.css` modules for the two integrations that import stylesheets, now that side-effect imports are checked. * fix(types): include web-shell client declarations in the build config The config's include listed only client/index.tsx, so client/vite-env.d.ts and client/local-files/file-system-access.d.ts never entered the program. Removing baseUrl unmasked 227 tsgo errors (and 222 under tsc 5.8) for the CSS module and File System Access declarations they provide. --------- Co-authored-by: Shaojin Wen --- integration-tests/cli/todo_write.test.ts | 4 ++-- .../helpers/hosted-broker-reply-loss-driver.ts | 2 +- integration-tests/helpers/hosted-process-crash-driver.ts | 2 +- integration-tests/tsconfig.json | 1 - integrations/generated-ui/src/app/css.d.ts | 8 ++++++++ integrations/test-failure-explorer/src/app/css.d.ts | 8 ++++++++ packages/acp-bridge/tsconfig.json | 1 - packages/channels/base/src/DaemonChannelBridge.ts | 2 +- packages/channels/dingtalk/src/outbound-file.ts | 2 +- packages/channels/dingtalk/src/outbound-image.ts | 2 +- packages/channels/weixin/src/api.ts | 2 +- packages/channels/weixin/src/media.ts | 5 ++++- packages/cli/src/agent-view/pty-host-process.ts | 2 +- packages/cli/src/serve/managed-external-publication.ts | 2 +- packages/cli/src/serve/remote-shell-result-publication.ts | 2 +- packages/cli/src/ui/opentui/item-projection.ts | 4 ++-- packages/cli/tsconfig.json | 1 - .../managed-runtime/http-managed-session-store.test.ts | 2 +- packages/core/src/omni/download.test.ts | 2 +- .../core/src/tools/tool-result-boundary-diagnostics.ts | 4 +++- packages/core/src/utils/git-branches.ts | 2 +- packages/cua-driver/typescript/tsconfig.json | 3 ++- packages/sdk-typescript/src/daemon/DaemonClient.ts | 2 +- packages/sdk-typescript/tsconfig.build.json | 1 + packages/sdk-typescript/tsconfig.json | 5 ++++- .../client/components/artifacts/artifactUtils.ts | 2 +- packages/web-shell/client/hooks/useAtMentionMenu.ts | 2 +- packages/web-shell/tsconfig.build.json | 2 +- packages/web-shell/tsconfig.json | 1 - packages/web-shell/tsconfig.lib.json | 1 + 30 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 integrations/generated-ui/src/app/css.d.ts create mode 100644 integrations/test-failure-explorer/src/app/css.d.ts diff --git a/integration-tests/cli/todo_write.test.ts b/integration-tests/cli/todo_write.test.ts index 99cf15bb65f..1c298dafac4 100644 --- a/integration-tests/cli/todo_write.test.ts +++ b/integration-tests/cli/todo_write.test.ts @@ -38,10 +38,10 @@ function inspectTodoRequests(requests: Array>) { const toolResult = requests .flatMap( (request) => - ((request?.['messages'] ?? []) as Array<{ + (request?.['messages'] ?? []) as Array<{ role?: string; content?: unknown; - }>) ?? [], + }>, ) .filter(({ role }) => role === 'tool') .map(({ content }) => JSON.stringify(content)) diff --git a/integration-tests/helpers/hosted-broker-reply-loss-driver.ts b/integration-tests/helpers/hosted-broker-reply-loss-driver.ts index 1c40eb4efa6..32b4a7c2f38 100644 --- a/integration-tests/helpers/hosted-broker-reply-loss-driver.ts +++ b/integration-tests/helpers/hosted-broker-reply-loss-driver.ts @@ -57,7 +57,7 @@ async function json(route: string, body?: unknown, expected = 200) { return text ? JSON.parse(text) : undefined; } -async function upstream(route: string, body?: Buffer) { +async function upstream(route: string, body?: Buffer) { const response = await fetch(new URL(route, config.brokerUrl), { method: body?.length ? 'POST' : 'GET', headers: { diff --git a/integration-tests/helpers/hosted-process-crash-driver.ts b/integration-tests/helpers/hosted-process-crash-driver.ts index 158980e55d9..1e8f1eb5b6f 100644 --- a/integration-tests/helpers/hosted-process-crash-driver.ts +++ b/integration-tests/helpers/hosted-process-crash-driver.ts @@ -68,7 +68,7 @@ let injected = false; let serviceKilled = false; let proxyFailure: unknown; let originalCancel: - | { path: string; headers: Record; body: Buffer } + | { path: string; headers: Record; body: Buffer } | undefined; let releaseFollowupModel!: () => void; const followupModel = new Promise((resolve) => { diff --git a/integration-tests/tsconfig.json b/integration-tests/tsconfig.json index 8f1de2aacc2..ee524096d05 100644 --- a/integration-tests/tsconfig.json +++ b/integration-tests/tsconfig.json @@ -18,7 +18,6 @@ // `terminal-capture/` and pulls SDK sources that reference `WebSocket` / // `HeadersInit`, none of which exist in the root's ES2023-only lib. "lib": ["DOM", "DOM.Iterable", "ES2023"], - "baseUrl": ".", // Resolve workspace packages from source so `tsc -p` here does not depend // on whether someone had built them recently — a missing dist used to fail // resolution outright and a stale one silently typechecked against old diff --git a/integrations/generated-ui/src/app/css.d.ts b/integrations/generated-ui/src/app/css.d.ts new file mode 100644 index 00000000000..91e56fe8476 --- /dev/null +++ b/integrations/generated-ui/src/app/css.d.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2026 Qwen Team + * SPDX-License-Identifier: Apache-2.0 + */ + +// The bundler inlines stylesheet imports; this lets TypeScript accept them. +declare module '*.css'; diff --git a/integrations/test-failure-explorer/src/app/css.d.ts b/integrations/test-failure-explorer/src/app/css.d.ts new file mode 100644 index 00000000000..91e56fe8476 --- /dev/null +++ b/integrations/test-failure-explorer/src/app/css.d.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2026 Qwen Team + * SPDX-License-Identifier: Apache-2.0 + */ + +// The bundler inlines stylesheet imports; this lets TypeScript accept them. +declare module '*.css'; diff --git a/packages/acp-bridge/tsconfig.json b/packages/acp-bridge/tsconfig.json index 9b696417c97..25e0709bd08 100644 --- a/packages/acp-bridge/tsconfig.json +++ b/packages/acp-bridge/tsconfig.json @@ -3,7 +3,6 @@ "compilerOptions": { "outDir": "dist", "rootDir": "src", - "baseUrl": ".", "lib": ["ES2023"], "composite": true, "types": ["node", "vitest/globals"], diff --git a/packages/channels/base/src/DaemonChannelBridge.ts b/packages/channels/base/src/DaemonChannelBridge.ts index 65d9c165da6..086f59ae110 100644 --- a/packages/channels/base/src/DaemonChannelBridge.ts +++ b/packages/channels/base/src/DaemonChannelBridge.ts @@ -210,7 +210,7 @@ function channelImageName(mimeType: string, index = 0): string | undefined { function decodeChannelImage( data: string, oversizedReason: string, -): { bytes: Buffer } | { skip: string } { +): { bytes: Buffer } | { skip: string } { // Valid base64 decodes to at most this many bytes, so an oversized image // is rejected on length alone instead of allocating a buffer the size // check would discard. Padding is subtracted only when the input length diff --git a/packages/channels/dingtalk/src/outbound-file.ts b/packages/channels/dingtalk/src/outbound-file.ts index 4535c9104e1..a3a28134e63 100644 --- a/packages/channels/dingtalk/src/outbound-file.ts +++ b/packages/channels/dingtalk/src/outbound-file.ts @@ -29,7 +29,7 @@ export interface FileProjection { } export interface ValidatedFile { - data: Buffer; + data: Buffer; fileName: string; fileType: string; } diff --git a/packages/channels/dingtalk/src/outbound-image.ts b/packages/channels/dingtalk/src/outbound-image.ts index 8f600bf4f37..0c93fdd40e4 100644 --- a/packages/channels/dingtalk/src/outbound-image.ts +++ b/packages/channels/dingtalk/src/outbound-image.ts @@ -22,7 +22,7 @@ export interface ImageMarker { } export interface ValidatedImage { - data: Buffer; + data: Buffer; fileName: string; mimeType: string; } diff --git a/packages/channels/weixin/src/api.ts b/packages/channels/weixin/src/api.ts index 41ce2a23551..fb336b1b9c5 100644 --- a/packages/channels/weixin/src/api.ts +++ b/packages/channels/weixin/src/api.ts @@ -338,7 +338,7 @@ export async function getUploadUrl( export async function uploadToCdn( urlOrParam: string, filekey: string, - encryptedData: Buffer, + encryptedData: Buffer, ): Promise { const CDN_HOST = 'novac2c.cdn.weixin.qq.com'; diff --git a/packages/channels/weixin/src/media.ts b/packages/channels/weixin/src/media.ts index 7e079a760a1..da238d84a06 100644 --- a/packages/channels/weixin/src/media.ts +++ b/packages/channels/weixin/src/media.ts @@ -63,7 +63,10 @@ export async function downloadAndDecrypt( } /** AES-128-ECB encryption for CDN upload. */ -export function encryptAesEcb(plaintext: Buffer, key: Buffer): Buffer { +export function encryptAesEcb( + plaintext: Buffer, + key: Buffer, +): Buffer { const cipher = createCipheriv('aes-128-ecb', key, null); return Buffer.concat([cipher.update(plaintext), cipher.final()]); } diff --git a/packages/cli/src/agent-view/pty-host-process.ts b/packages/cli/src/agent-view/pty-host-process.ts index d7098c91c55..223c31d1cb3 100644 --- a/packages/cli/src/agent-view/pty-host-process.ts +++ b/packages/cli/src/agent-view/pty-host-process.ts @@ -669,7 +669,7 @@ export function createAgentViewPtyHostServer( }); // Byte-transparent framing: only the request line is decoded, so // coalesced keystrokes after an attach request reach the worker verbatim. - let buffer = Buffer.alloc(0); + let buffer: Buffer = Buffer.alloc(0); socket.on('data', (chunk: Buffer) => { buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]); if (buffer.length > MAX_PTY_HOST_REQUEST_LINE_BYTES) { diff --git a/packages/cli/src/serve/managed-external-publication.ts b/packages/cli/src/serve/managed-external-publication.ts index 51ed5f54bc8..d9de2c2e90b 100644 --- a/packages/cli/src/serve/managed-external-publication.ts +++ b/packages/cli/src/serve/managed-external-publication.ts @@ -306,7 +306,7 @@ export class ManagedExternalPublication READ_BYTES, Math.max(0, stream.byteLength - offset), ); - let bytes = Buffer.alloc(0); + let bytes: Buffer = Buffer.alloc(0); if (length) { const result = await channel.store.readRange({ manifestRef: manifest.ref, diff --git a/packages/cli/src/serve/remote-shell-result-publication.ts b/packages/cli/src/serve/remote-shell-result-publication.ts index 44f21ed0f58..57cf5c70ffb 100644 --- a/packages/cli/src/serve/remote-shell-result-publication.ts +++ b/packages/cli/src/serve/remote-shell-result-publication.ts @@ -176,7 +176,7 @@ class PublicationClient { 'Cache-Control': 'no-store', ...headers, }, - ...(body ? { body } : {}), + ...(body ? { body: body as BodyInit } : {}), redirect: 'error', signal: AbortSignal.timeout(30_000), }); diff --git a/packages/cli/src/ui/opentui/item-projection.ts b/packages/cli/src/ui/opentui/item-projection.ts index 5d0128247ff..a84bfa39ce7 100644 --- a/packages/cli/src/ui/opentui/item-projection.ts +++ b/packages/cli/src/ui/opentui/item-projection.ts @@ -1046,11 +1046,11 @@ export function projectSpecialItemText( ); case 'tools_list': return projectToolsList( - ((record['tools'] ?? []) as Array<{ + (record['tools'] ?? []) as Array<{ name: string; displayName?: string; description?: string; - }>) ?? [], + }>, Boolean(record['showDescriptions']), ); case 'model_stats': { diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index ad43a766e90..1eccc9315e6 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -3,7 +3,6 @@ "compilerOptions": { "outDir": "dist", "jsx": "react-jsx", - "baseUrl": ".", "lib": ["DOM", "DOM.Iterable", "ES2023"], "types": ["node", "vitest/globals"], "paths": { diff --git a/packages/core/src/managed-runtime/http-managed-session-store.test.ts b/packages/core/src/managed-runtime/http-managed-session-store.test.ts index 20218120df8..5aa185c6b46 100644 --- a/packages/core/src/managed-runtime/http-managed-session-store.test.ts +++ b/packages/core/src/managed-runtime/http-managed-session-store.test.ts @@ -8028,7 +8028,7 @@ class FakeManagedSessionStore { private readonly resources = new Map< string, { - bytes: Buffer; + bytes: Buffer; kind: string; schemaVersion: number; digest: string; diff --git a/packages/core/src/omni/download.test.ts b/packages/core/src/omni/download.test.ts index 645fdb00a75..415b2bd41c2 100644 --- a/packages/core/src/omni/download.test.ts +++ b/packages/core/src/omni/download.test.ts @@ -125,7 +125,7 @@ const respondWith = (body: BodyInit | null, init?: ResponseInit) => vi.fn(async () => new Response(body, init)) as unknown as typeof fetch; function fetchOk( - body: Buffer | string, + body: Buffer | string, headers: Record = {}, ): typeof fetch { return vi.fn(async () => new Response(body, { status: 200, headers })); diff --git a/packages/core/src/tools/tool-result-boundary-diagnostics.ts b/packages/core/src/tools/tool-result-boundary-diagnostics.ts index 1d4420ec86a..b02c2e88843 100644 --- a/packages/core/src/tools/tool-result-boundary-diagnostics.ts +++ b/packages/core/src/tools/tool-result-boundary-diagnostics.ts @@ -203,7 +203,9 @@ export function createToolResultBoundaryObserver( (logger === boundaryLogger ? isToolResultBoundaryDiagnosticsEnabled : () => isDebugLogFileEnabled() && logger.isEnabled()); - let hmacKey = options.hmacKey ? Buffer.from(options.hmacKey) : undefined; + let hmacKey: Buffer | undefined = options.hmacKey + ? Buffer.from(options.hmacKey) + : undefined; const thresholdBytes = options.thresholdBytes ?? TOOL_RESULT_BOUNDARY_JSON_BYTE_THRESHOLD; const logLimit = options.logLimit ?? TOOL_RESULT_BOUNDARY_LOG_LIMIT; diff --git a/packages/core/src/utils/git-branches.ts b/packages/core/src/utils/git-branches.ts index 6e70002559b..01923355fe5 100644 --- a/packages/core/src/utils/git-branches.ts +++ b/packages/core/src/utils/git-branches.ts @@ -214,7 +214,7 @@ export function streamGitRecords( } return true; }; - let pending = Buffer.alloc(0); + let pending: Buffer = Buffer.alloc(0); child.stdout.on('data', (chunk: Buffer) => { if (settled) return; pending = pending.length ? Buffer.concat([pending, chunk]) : chunk; diff --git a/packages/cua-driver/typescript/tsconfig.json b/packages/cua-driver/typescript/tsconfig.json index c6a04054ce7..81f6c0d1cf1 100644 --- a/packages/cua-driver/typescript/tsconfig.json +++ b/packages/cua-driver/typescript/tsconfig.json @@ -10,7 +10,8 @@ "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "skipLibCheck": true, - "verbatimModuleSyntax": true + "verbatimModuleSyntax": true, + "types": ["node"] }, "include": ["src/**/*.ts"] } diff --git a/packages/sdk-typescript/src/daemon/DaemonClient.ts b/packages/sdk-typescript/src/daemon/DaemonClient.ts index e5f220d0fd0..ba53910eca7 100644 --- a/packages/sdk-typescript/src/daemon/DaemonClient.ts +++ b/packages/sdk-typescript/src/daemon/DaemonClient.ts @@ -2676,7 +2676,7 @@ export class DaemonClient { { method: 'POST', headers, - body: req.data, + body: req.data as BodyInit, ...(req.signal ? { signal: req.signal } : {}), }, async (res) => { diff --git a/packages/sdk-typescript/tsconfig.build.json b/packages/sdk-typescript/tsconfig.build.json index 6cee9e28d71..8f7dab89a96 100644 --- a/packages/sdk-typescript/tsconfig.build.json +++ b/packages/sdk-typescript/tsconfig.build.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./dist", + "rootDir": "./src", "module": "ESNext", "moduleResolution": "bundler", "declaration": true, diff --git a/packages/sdk-typescript/tsconfig.json b/packages/sdk-typescript/tsconfig.json index b24c49cd17f..012e277cf60 100644 --- a/packages/sdk-typescript/tsconfig.json +++ b/packages/sdk-typescript/tsconfig.json @@ -11,6 +11,10 @@ "declarationMap": true, "sourceMap": true, "outDir": "./dist", + // The core sources mapped in `paths` below sit outside this package, so the + // project root is the packages directory. TypeScript 5 infers that; newer + // compilers default rootDir to this directory and reject those files. + "rootDir": "..", "removeComments": true, "importHelpers": false, @@ -35,7 +39,6 @@ /* Module Resolution */ "resolveJsonModule": true, "types": ["node"], - "baseUrl": ".", // The cli bundle pulls these sources in through the cli tsconfig // `@qwen-code/sdk/*` mapping, and mainBuild discovers this file per // importing file (it passes no `tsconfig` option). Without a named entry diff --git a/packages/web-shell/client/components/artifacts/artifactUtils.ts b/packages/web-shell/client/components/artifacts/artifactUtils.ts index a48b4bb8389..b51c801bce0 100644 --- a/packages/web-shell/client/components/artifacts/artifactUtils.ts +++ b/packages/web-shell/client/components/artifacts/artifactUtils.ts @@ -287,7 +287,7 @@ export async function readWorkspaceFileAsBlob( maxBytes?: number; }, ): Promise { - const chunks: Uint8Array[] = []; + const chunks: Array> = []; const maxBytes = options.maxBytes ?? MAX_WORKSPACE_FILE_BLOB_BYTES; const initialStat = await options.statFile(filePath); if (options.isCancelled?.()) { diff --git a/packages/web-shell/client/hooks/useAtMentionMenu.ts b/packages/web-shell/client/hooks/useAtMentionMenu.ts index 7a0421ca5ec..fd11c06f6ec 100644 --- a/packages/web-shell/client/hooks/useAtMentionMenu.ts +++ b/packages/web-shell/client/hooks/useAtMentionMenu.ts @@ -212,7 +212,7 @@ function getProviderTextValue(provider: WebShellAtProvider): string { ? undefined : sanitizeDisplayText(provider.textValue)) ?? (typeof provider.label === 'string' - ? (sanitizeDisplayText(provider.label) ?? undefined) + ? sanitizeDisplayText(provider.label) : undefined) ?? safeDisplayText(provider.id) ?? SAFE_DISPLAY_FALLBACK diff --git a/packages/web-shell/tsconfig.build.json b/packages/web-shell/tsconfig.build.json index 52173ffff5e..1a5dfd3876d 100644 --- a/packages/web-shell/tsconfig.build.json +++ b/packages/web-shell/tsconfig.build.json @@ -7,5 +7,5 @@ "outDir": "dist", "rootDir": "client" }, - "include": ["client/index.tsx"] + "include": ["client/index.tsx", "client/**/*.d.ts"] } diff --git a/packages/web-shell/tsconfig.json b/packages/web-shell/tsconfig.json index b7d9a6b15d3..22157ca95c3 100644 --- a/packages/web-shell/tsconfig.json +++ b/packages/web-shell/tsconfig.json @@ -4,7 +4,6 @@ "module": "ESNext", "moduleResolution": "bundler", "jsx": "react-jsx", - "baseUrl": ".", "paths": { "@qwen-code/web-shell/daemon-react-sdk": ["./client/daemon-react-sdk.ts"], "@qwen-code/web-shell/transcript": ["./client/transcript.ts"], diff --git a/packages/web-shell/tsconfig.lib.json b/packages/web-shell/tsconfig.lib.json index b1e22f68343..fa08ca6a08b 100644 --- a/packages/web-shell/tsconfig.lib.json +++ b/packages/web-shell/tsconfig.lib.json @@ -5,6 +5,7 @@ "stripInternal": true, "emitDeclarationOnly": true, "declarationDir": "dist/types", + "rootDir": "client", "noEmit": false }, "include": ["client/**/*.ts", "client/**/*.tsx"],