什么是 declare 关键字?
下文默认基于 TypeScript 5.x。
面试速答(30 秒版 TL;DR)
declare的作用是:告诉 TypeScript“这个东西在运行时会存在,但实现不在当前文件里”。- 它主要用于类型声明,而不是写运行时逻辑。
- 常见场景:
.d.ts声明文件;- 给第三方 JS 库补类型;
- 声明全局变量、全局函数、模块、命名空间;
- 扩展
window、globalThis等全局对象类型。
- 一句话记忆:
declare负责“告诉编译器有这回事”;- 不负责“真的创建这个值”。
1. 为什么需要 declare
TypeScript 做类型检查时,只能根据它“看得见”的声明来判断。
但工程里经常会遇到这类情况:
- 运行时脚本已经往全局挂了变量;
- 第三方 JS 包能用,但没类型;
- 某个对象由宿主环境提供,比如浏览器、Node、打包器注入值。
这时如果没有类型声明,TS 就不知道它的存在。
2. 最简单的例子:声明一个全局变量
declare const APP_VERSION: string;
console.log(APP_VERSION);
它的含义是:
- 编译器可以把
APP_VERSION当成string看; - 但这行代码不会在运行时创建变量。
所以如果真实环境里没有这个值,运行时照样会报错。
3. declare 最常见的宿主:.d.ts 文件
例如:
declare module "legacy-sdk" {
export function init(token: string): void;
export function request(path: string): Promise<unknown>;
}
这类文件的定位就是:
- 不写实现;
- 只写类型契约;
- 让 TypeScript 知道怎么检查你的调用代码。
4. 声明全局对象扩展也是高频题
例如给 window 补一个字段:
declare global {
interface Window {
__APP_CONFIG__: {
apiBase: string;
env: "dev" | "prod";
};
}
}
然后你就可以:
window.__APP_CONFIG__.apiBase;
这类写法在:
- 老项目接全局脚本;
- SSR 注水数据;
- 微前端主子应用通信;
都很常见。
5. declare 能声明哪些东西
常见包括:
declare constdeclare letdeclare functiondeclare classdeclare namespacedeclare moduledeclare global
例如:
declare function track(eventName: string, payload?: Record<string, unknown>): void;
declare class Player {
play(): void;
pause(): void;
}
6. declare 和真正实现的区别
看下面两段代码:
declare function sum(a: number, b: number): number;
function sum(a: number, b: number) {
return a + b;
}
区别在于:
- 第一段只有声明,没有实现;
- 第二段既给类型,也给运行时代码。
所以 declare 很像在说:
- “你先相信运行时会有这个东西,我这里只补类型信息。”
7. 高频面试题标准答法
7.1 declare 会不会生成 JavaScript 代码
通常不会。
它的核心用途就是类型声明,不是生成实现。
7.2 declare 和 as 有什么区别
declare:给某个符号补“外部存在”的类型声明;as:对某个已有值做类型断言。
一个偏“声明符号”,一个偏“断言值”。
7.3 为什么第三方库经常有 .d.ts
因为很多库需要把“可被外部消费的 API 结构”暴露给 TypeScript 用户,而不需要把源码细节都暴露出来。
8. 常见误区
- 误区 1:以为
declare会创建变量或函数。- 不会,它只告诉编译器。
- 误区 2:觉得写了
declare就绝对安全。- 如果运行时没有实现,依然会崩。
- 误区 3:把
declare当成“任何缺类型问题的万能药”。- 声明写错了,本质上是在欺骗编译器。
速记要点
declare= 声明运行时存在、当前文件不实现的符号。- 典型场景是
.d.ts、第三方库补类型、全局变量扩展。 - 它只影响编译期,不负责生成运行时实现。