阮一峰《TypeScript 教程》学习笔记——declare关键字
1. 一段话总结
declare关键字是TypeScript中用于告知编译器“外部已存在特定类型”的纯类型声明工具,仅提供类型描述(不包含具体实现),所有declare语句不进入编译产物,核心用途是解决“引用外部类型(如第三方库、全局对象)时的编译报错”;它支持描述7种类型场景:变量(variable)、函数(function)、类(class)、模块/命名空间(module/namespace)、全局对象扩展(global)、枚举(enum) ,以及用于整合模块类型的类型声明文件(.d.ts),且仅能描述已有类型,无法声明新类型。
2. 思维导图

3. 详细总结
一、declare 关键字简介
| 核心维度 | 说明 |
|---|---|
| 核心作用 | 告知TypeScript编译器“外部已存在某类型”,避免引用外部类型时的编译报错 |
| 本质 | 纯类型声明工具,仅提供类型描述,不包含具体实现 |
| 编译特性 | 所有declare语句不进入最终编译产物(仅用于编译时类型检查) |
| 支持场景 | 变量、函数、类、模块、命名空间、全局对象扩展、枚举(共7种核心场景) |
| 限制 | 仅描述已有类型,无法声明新类型(如不能用declare定义新接口) |
二、各场景具体用法
1. declare variable(描述外部变量)
- 作用:定义外部全局变量或第三方库暴露的变量类型。
- 关键规则:
- 不可设置初始值(仅类型描述,涉及值会报错);
- 未显式指定类型时,变量默认类型为
any;
- 示例代码:
declare let x:number; // 正确:描述外部变量x为number类型 declare var document; // 正确:document类型为any(未指定类型) declare let y:number = 1; // 报错:不可设置初始值
2. declare function(描述外部函数)
- 作用:定义外部函数的参数类型与返回值类型。
- 关键规则:
- 仅描述函数类型,无函数体实现;
- TypeScript不支持单独的函数类型声明(必须结合declare);
- 示例代码:
// 正确:描述sayHello函数的类型 declare function sayHello(name:string):void; sayHello('张三'); // 编译通过(外部已实现该函数)// 报错:单独写函数类型声明(无declare) function sayHi(name:string):void;
3. declare class(描述外部类)
- 作用:定义外部类的完整结构(构造器、属性、方法、存取器等)。
- 支持描述的类成员:
- 静态成员(static)、实例属性(public/private);
- 构造器(constructor)、实例方法、存取器(get/set);
- 索引签名(如
[index:string]:any);
- 关键规则:无类的具体实现(如方法体、属性初始值);
- 示例代码:
declare class Animal {constructor(name:string); // 构造器类型public eat():void; // 实例方法private sleep():void; // 私有实例方法static count:number; // 静态属性get age():number; // 取值器set age(value:number); // 存值器 }
4. declare module / declare namespace(组织外部类型)
- 作用:将外部模块或命名空间的类型整合,支持扩展外部模块类型。
- 核心差异与规则:
类型 核心用途 特殊规则 declare namespace 组织全局类型(如第三方库全局对象) 内部可加/不加export,成员默认可访问 declare module 组织模块类型(如ES模块) 支持模块名通配符(如 my-plugin-*),可扩展已有模块类型(同名接口自动合并) - 扩展外部模块类型示例:
// 从moduleA导入Foo接口,扩展其类型 import { Foo } from './moduleA'; declare module './moduleA' {interface Foo { // 同名接口自动合并,扩展属性custom: { prop1:string };} } const a:Foo = { x:0, custom: { prop1:'a' } }; // 编译通过
5. declare global(扩展全局对象)
- 作用:为JavaScript原生全局对象(如
String、window)添加新属性/方法的类型。 - 关键规则:
- 必须在模块文件中使用(需加空导出
export {}强制视为模块); - 仅能扩充现有全局对象的类型,不能新增顶层全局类型;
- 必须在模块文件中使用(需加空导出
- 示例代码:
export {}; // 强制视为模块 declare global {// 为String原型添加toSmallString方法类型interface String {toSmallString():string;}// 为window添加myAppConfig属性类型interface Window {myAppConfig: { apiUrl:string };} } // 实现新增方法(类型已通过declare global声明) String.prototype.toSmallString = () => 'small'; const config = window.myAppConfig; // 编译通过
6. declare enum(描述外部枚举)
- 作用:定义外部枚举的类型(如第三方库暴露的枚举)。
- 支持的枚举形式:
枚举类型 语法示例 特点 普通declare enum declare enum E1 { A, B }描述外部普通枚举 显式值declare enum declare enum E2 { A=0, B=1 }枚举成员显式赋值 declare const enum declare const enum E3 { A, B }描述外部常量枚举 - 关键规则:无枚举的具体实现,仅提供成员类型描述。
7. declare 用于类型声明文件(.d.ts)
- 作用:在单个
.d.ts文件中整合多个外部模块的类型(如Node.js的node.d.ts),避免每个文件单独声明。 - 示例结构(
node.d.ts部分内容):// 声明url模块类型 declare module "url" {export interface Url { protocol?:string; hostname?:string; }export function parse(urlStr:string):Url; } // 声明path模块类型 declare module "path" {export function join(...paths:any[]):string;export var sep:string; } - 引用方式:通过三斜杠命令加载类型声明文件:
/// <reference path="node.d.ts"/> // 加载类型声明 import * as url from 'url'; // 编译通过
4. 关键问题
问题1:declare关键字的核心特性与普通类型声明(如interface、type)的本质区别是什么?
答案:
核心区别体现在“作用对象”“实现要求”“编译产物”三个维度:
- 作用对象:declare仅描述外部已存在的类型(如第三方库、全局对象),不创建新类型;普通类型声明(interface/type)用于定义新类型(供当前项目使用);
- 实现要求:declare仅提供类型描述,无任何具体实现(如函数无体、类无方法逻辑);普通类型声明需配合具体实现(如class需写方法体);
- 编译产物:declare语句不进入最终编译产物(仅用于编译时检查);普通类型声明虽也不生成代码,但依赖其的业务代码会被编译(如用interface约束的变量会保留逻辑)。
问题2:如何使用declare扩展外部模块的类型或JavaScript原生全局对象的类型?请分别举例说明。
答案:
需根据“扩展目标”选择不同declare语法:
-
扩展外部模块类型:使用
declare module "模块名",利用“同名接口自动合并”扩展类型,示例:// 1. 导入外部模块的原有类型 import { Foo } from './moduleA'; // 2. 用declare module扩展该模块类型 declare module './moduleA' {interface Foo { // 同名接口自动合并,新增custom属性custom: { prop1:string };} } // 3. 使用扩展后的类型(编译通过) const a:Foo = { x:0, custom: { prop1:'test' } }; -
扩展全局对象类型:使用
declare global(需在模块中,加export {}),示例:// 1. 空导出强制视为模块(declare global需在模块中) export {}; // 2. 扩展String原型方法类型 declare global {interface String {toSmallString():string; // 新增方法类型} } // 3. 实现新增方法(类型已通过declare声明) String.prototype.toSmallString = () => this.toLowerCase(); // 4. 使用扩展后的方法(编译通过) 'HELLO'.toSmallString(); // 'hello'
问题3:declare enum与普通enum(无declare)的核心差异是什么?在什么场景下需要使用declare enum?
答案:
两者核心差异及适用场景如下:
| 对比维度 | declare enum | 普通enum |
|---|---|---|
| 作用 | 描述外部已存在的枚举(如第三方库暴露的枚举) | 定义当前项目的新枚举 |
| 实现要求 | 仅提供枚举成员类型,无具体实现 | 需定义枚举成员值(默认递增或显式赋值) |
| 编译产物 | 不生成枚举相关代码(仅类型描述) | 生成JavaScript对象(保留枚举逻辑) |
适用场景:仅当需要引用“外部已实现的枚举”(如第三方库通过脚本暴露的全局枚举)时,使用declare enum;例如:
// 第三方库已在全局暴露枚举E(如window.E)
declare enum E { A=0, B=1 }
// 当前项目使用该枚举(编译通过,依赖外部实现)
const val:E = E.A;
若枚举是当前项目定义的,则用普通enum,无需declare。
