声明文件
.d.ts 文件是 TypeScript 的类型声明文件(Type Declaration File)
d.ts文件
d.ts是纯粹的类型声明文件,不产出任何 JS 代码。
声明文件的作用:
提供类型给没有类型的 JS 文件
比如你写的是 math.js,JS 本身没有类型,写一个 math.d.ts 可以给 TS 提示类型。
对外暴露库的类型声明
如果你写的是一个 npm 包,编译后会产生一个d.ts文件和js文件。d.ts提供给开发者阅读。TS源码将不会泄露
模块增强或全局声明
给现有模块加类型,或者声明全局函数/变量。
安装第三方类型文件
如果第三方库不存在类型声明文件,可以尝试使用 @types/模块名 安装
pnpm i @types/lodash -Dts文件类型
Script (脚本模式)
- ts文件中没有 import 或 export 语句的文件,默认就是脚本模式。
- 文件里的顶级变量、声明会被认为是 全局的,可能会和别的文件冲突。
假如你有两个文件:
// a.ts
interface User {
name: string;
}// b.ts
interface User {
id: number;
}interface User {
name: string;
id: number;
}因为它们都是 脚本模式,TypeScript 会自动把这两个 User 接口合并到全局里,导致奇怪的类型混淆。
Module(模块模式)
- 只要文件中出现了 import 或 export,它就会被当成模块。
- 模块里的顶级变量、函数、接口都只在这个模块作用域里生效,不会污染全局。
- 其他文件没有import导入该文件,那么该文件就不会被TS编译。
Script -> Module
写 export {}; 就能强制把文件标记为模块。
// types/user.d.ts
export {}; // ← 文件变成模块
interface User { // ← 既没 export,也不在全局
name: string;
}declare
declare 是一个关键字,用于声明某个标识符(变量、函数、类、模块等)的存在,但不给出具体实现。它告诉 TypeScript:“这个东西是存在的,但它在别的地方定义,你别报错。”。declare关键字可以在ts文件或者.d.ts中使用。
声明变量 / 常量
declare const VERSION: string;
declare let count: number;
declare var window: Window;声明函数
declare function alert(message?: any): void;
declare function sum(a: number, b: number): number;声明全局类型
export {}
declare global {
interface Window {
myAppVersion: string;
}
}脚本模式下会忽略declare global{},因为脚本模式就已经是全局作用域了,TS编译器不会认识declare global{},因此需要将脚本模式切换成模块模式,全局类型声明才会生效。你也可以直接在脚本模式下声明全局类型:
interface Window {
myAppVersion: string;
}声明模块
- 声明全局式模块类型
// types/a.d.ts 全局声明
declare module 'my-lib' {
export function f1(): void;
}只要 types/a.d.ts 在 tsconfig.include 里,任何地方 import { f1 } from 'my-lib' 都能用。
- 声明可合并式模块类型
如果 没有任何 TS 文件写 import './types/b' 或 import 'types/b',那么 f2 永远不存在,会报 TS2305: Module '"my-lib"' has no exported member 'f2'.
// types/b.d.ts 模块级扩充
export {}; // 或任何 import/export
declare module 'my-lib' {
export function f2(): void;
}// components.d.ts
declare module 'vue' {
export interface GlobalComponents {
CaseButton: typeof import('./../components/CaseButton.vue')['default']
}
}
// 🔑 关键:让本文件成为「模块」,避免污染全局
export {}没有 export {} 时,TS 会把它当成“全局声明”,后续 runtime-dom 再想写 declare module 'vue' { ... } 就会冲突; 加了 export {},文件处于 模块作用域,同名模块声明就能被 TS 合并(augment)——这正是 Vue 想要的“插件式”类型扩充机制。
TypeScript 怎么理解模块?
当你写:
import { doSomething } from 'my-module';TypeScript 会去查:
- 是否是ts文件
- 是否存在declare module 'my-module'
- 里面有没有 导出
doSomething的类型信息?
如果没有,TS 就会报错:“找不到模块 ‘my-module’ 的类型定义。”
declare module 里写的类型需要和模块中对应的成员名字相同,类型的导出方式要和模块中的导出方式相同。这样TS系统就能识别模块中成员的类型。
声明接口或类型
// types.d.ts
declare interface Window {
__INITIAL_DATA__: any;
}示例
// 导入express模块
import express from 'express';
// 创建一个express应用实例
const app = express();
// 设置一个路由,响应根路径的GET请求
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
// 监听端口3000,启动服务器
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});declare module 'express' {
// 定义 express 相关类型
export type RequestHandler = (req: Request, res: Response, next: NextFunction) => void;
export interface Request {
body: any;
params: { [key: string]: string };
query: { [key: string]: string };
// 其他常用属性可以继续扩展...
}
export interface Response {
send: (body: any) => this;
status: (code: number) => this;
json: (data: any) => this;
// 其他常用方法可以继续扩展...
}
export interface NextFunction {
(): void;
}
export interface Express {
get: (path: string, handler: RequestHandler) => void;
listen: (port: number, callback: () => void) => void;
use: (middleware: RequestHandler) => void;
}
export function express(): Express;
}