Readable.fromWeb(response.body) 类型报错?import * as class streamstream from 'node:stream'
const const response: Responseresponse = await function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)fetch('https://example.com')
if (const response: Responseresponse.Body.body: ReadableStream<Uint8Array<ArrayBuffer>> | nullbody) {
class streamstream.class Stream.ReadableReadable.Stream.Readable.fromWeb(readableStream: ReadableStream, options?: Pick<stream.ReadableOptions, "encoding" | "highWaterMark" | "objectMode" | "signal">): stream.ReadablefromWeb(response.Body.body: ReadableStream<Uint8Array<ArrayBuffer>>body)}观察 fetch 的类型,会发现他来自 lib.dom.d.ts,可是哪一行代码引入了 DOM 类型呢?
// tsconfig 里也没写 DOM ... 吗
{
"compilerOptions": {
"types": ["node"]
}
}其实,不写 "lib" 等于引入了默认 lib,也就是 typescript/lib/lib.d.ts:
/// <reference lib="es5" />
/// <reference lib="dom" />
/// <reference lib="webworker.importscripts" />
/// <reference lib="scripthost" />所以一个显而易见的修复方案是,手动指定一下 lib:
{
"compilerOptions": {
"lib": ["ES2024"], // <-- 没有 DOM
"types": ["node"]
}
}但是这个办法只能在所有文件都是 Node.js 环境的才可以,实际项目可能组织成下面这样:
src/vs/base/common/disposable.tssrc/vs/base/node/fs.tssrc/vs/base/browser/dom.tssrc/tsconfig.json (管所有文件)简单解释一下这个结构:
{源码文件夹}/{独立模块}/{运行环境}/{代码}.ts
独立模块们可能是由不同人开发的,但是相比 monorepo 里开一堆包,这个结构不需要额外写一大堆内容大部分重复的 package.json,也不需要在每个子包里精确管理依赖(只要最顶层这个包的依赖完整即可)。
根据运行环境分隔代码的好处是,开发者可以编写简单的 lint 插件 避免例如前端的代码引用了后端的问题。需要注意的是这种风格下最好避免使用 Barrel files (index.ts)。
这种情况下就不能避免 DOM 类型被引入了,解法是我们强行让 DOM 里的 Response 兼容 Node.js 里的:
import * as class streamstream from 'node:stream'
// 注意到 DOM 里的 Response 是全局的, 可以通过 declare global 来修改他
import * as module "stream/web"streamWeb from 'stream/web'
declare global {
interface Response {
Response.body: streamWeb.ReadableStream<Uint8Array<ArrayBufferLike>> | nullbody: module "stream/web"streamWeb.interface ReadableStream<R = any>ReadableStream<interface Uint8Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>Uint8Array> | null
}
}
const const response: Responseresponse = await function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)fetch('https://example.com')
if (const response: Responseresponse.Response.body: streamWeb.ReadableStream<Uint8Array<ArrayBufferLike>> | nullbody) {
class streamstream.class Stream.ReadableReadable.Stream.Readable.fromWeb(readableStream: streamWeb.ReadableStream, options?: Pick<stream.ReadableOptions, "encoding" | "highWaterMark" | "objectMode" | "signal">): stream.ReadablefromWeb(const response: Responseresponse.Response.body: streamWeb.ReadableStream<Uint8Array<ArrayBufferLike>>body)
}