关于TypeScript创建Chrome扩展content scripts专属d.ts文件的技术问询
作为TypeScript初/中级用户,我正在为Chrome扩展的Content Scripts场景创建global.d.ts文件,因为Content Scripts只能访问有限的扩展API子集。以下是我的初步尝试代码:
export {} declare global { interface Window { chrome: Chrome } const chrome: Chrome // 后续会针对此行提问 } interface Chrome { runtime: import('../node_modules/@types/chrome/index.d.ts').runtime // 我知道这语法不对,Content Scripts的chrome.runtime只是完整API的子集,这里仅作示意 // 还需要添加storage、dom、i18n等命名空间 }
我之前仅少量接触d.ts文件(已为扩展服务工作者上下文创建了单独的global.d.ts),希望解答以下问题:
- 是否已存在Content Scripts专属的类型库?我在DefinitelyTyped中未找到;
- d.ts文件能否导入另一个d.ts文件的部分类型?我不想重复编写已有类型;
- 示例:如何声明全局的
[window.]chrome.runtime.sendMessage并重用@types/chrome中的chrome.runtime.sendMessage类型? - 如何排除
@types/chrome中其余的全局/命名空间?@types/chrome/index.d.ts中包含interface Window { chrome: typeof chrome; }; - 附带问题:Content Scripts运行在DOM上下文(即
globalThis为Window),但我发现仅通过declare global { interface Window { foo: string } }时,VSCode无法解析全局foo,必须通过window.foo访问或额外声明const foo: string,这是为什么?
问题解答
1. 是否存在Content Scripts专属的类型库?
目前DefinitelyTyped上没有专门为Content Scripts拆分的独立类型库,@types/chrome提供的是Chrome扩展全API的类型定义,覆盖了背景页、Popup、Content Scripts等所有场景的API。如果需要适配Content Scripts的受限API,只能基于@types/chrome自行裁剪类型。
2. d.ts文件能否导入另一个d.ts文件的部分类型?
可以,TypeScript支持类型导入语法,无需导入整个模块就能复用目标d.ts中的特定类型,常用两种写法:
- 直接在类型定义中使用
import(...)类型查询 - 顶部用
import type导入具体类型
示例:
// 方式1:类型查询 interface ChromeRuntime { sendMessage: import('chrome').runtime.sendMessage } // 方式2:显式类型导入 import type { runtime } from 'chrome'; type ChromeRuntimePick = Pick<typeof runtime, 'sendMessage' | 'getURL'>;
3. 如何声明全局的[window.]chrome.runtime.sendMessage并重用已有类型?
通过类型导入提取@types/chrome中的sendMessage类型,定义Content Scripts可用的Chrome子集接口,最后声明全局变量和Window接口属性即可:
export {} // 从@types/chrome导入所需的类型片段 import type { runtime, storage } from 'chrome'; // 定义仅包含Content Scripts可用API的Chrome接口 interface Chrome { runtime: Pick<typeof runtime, 'sendMessage' | 'getURL' | 'onMessage'> storage: Pick<typeof storage, 'local'> // 按需添加i18n、dom等其他可用API } declare global { // 声明window上的chrome属性 interface Window { chrome: Chrome } // 声明全局chrome变量,支持直接访问chrome.runtime.sendMessage const chrome: Chrome }
这样既重用了@types/chrome的原有类型,又只保留了Content Scripts能访问的API子集。
4. 如何排除@types/chrome中其余的全局/命名空间?
@types/chrome会默认在全局注入完整的chrome类型和Window接口扩展,要覆盖这个行为可按以下步骤操作:
- 在
tsconfig.json中,将types字段设为[],避免自动导入所有全局类型:{ "compilerOptions": { "types": [] } } - 在自己的
global.d.ts中,通过import type手动导入需要的类型片段,再重新声明全局的chrome变量和Window接口,自己的类型定义会覆盖@types/chrome的全局注入。 - 若仍有局部冲突,可临时用
// @ts-expect-error忽略,但不推荐长期使用。
5. 为什么仅声明Window中的foo无法直接访问全局foo?
TypeScript的类型系统不会自动将Window接口的自定义属性提升为全局变量,只有浏览器原生的全局属性(比如document、console)会被默认识别为全局变量。
要实现直接访问全局foo,必须在declare global块中额外声明全局变量:
export {} declare global { interface Window { foo: string } // 声明全局变量foo,让TypeScript识别为全局可访问 const foo: string }
否则只能通过window.foo访问自定义的Window属性。
内容的提问来源于stack exchange,提问作者crunk1

