第10章 类型声明与声明文件
本章定位:学习TypeScript的类型声明系统,包括
.d.ts声明文件的编写、内置类型声明的使用、DefinitelyTyped社区类型定义,以及如何创建自定义类型声明。
10.1 .d.ts声明文件的作用
Section titled “10.1 .d.ts声明文件的作用”什么是声明文件
Section titled “什么是声明文件”声明文件(Declaration Files)是以.d.ts为扩展名的文件,用于为JavaScript代码提供类型信息。TypeScript可以通过声明文件了解JavaScript库的类型结构。
function add(a, b) { return a + b;}module.exports = { add };
// math.d.tsexport function add(a: number, b: number): number;声明文件与编译
Section titled “声明文件与编译”// 没有声明文件时import { add } from "./math";add("1", "2"); // 编译时没有类型检查
// 有声明文件时add("1", "2"); // 编译错误!Argument of type 'string' is not assignable to parameter of type 'number'全局声明文件
Section titled “全局声明文件”当TypeScript项目中找到顶级.d.ts文件,会将其内容作为全局声明:
// global.d.ts(放在项目根目录或src目录)declare const APP_VERSION: string;declare function formatDate(date: Date): string;
interface Config { apiUrl: string; timeout: number;}declare const config: Config;10.2 内置类型声明
Section titled “10.2 内置类型声明”JavaScript运行时类型
Section titled “JavaScript运行时类型”TypeScript包含JavaScript标准库的类型声明:
// String"hello".toUpperCase();String.prototype.trim();
// NumberMath.PI;Number.parseInt("42");
// Booleanconst flag: boolean = true;
// Arrayconst arr: number[] = [1, 2, 3];arr.map(x => x * 2);
// ObjectObject.keys({ a: 1, b: 2 });
// Promiseconst promise = new Promise<string>((resolve) => { resolve("done");});DOM类型声明
Section titled “DOM类型声明”// Windowwindow.addEventListener("click", (event) => { console.log(event.clientX, event.clientY);});
// Documentdocument.createElement("div");document.querySelector(".container");
// HTMLElementconst element = document.getElementById("myInput") as HTMLInputElement;element.value;在tsconfig.json中指定包含的库:
{ "compilerOptions": { "lib": ["ES2020", "DOM", "DOM.Iterable"] }}常用lib选项:
"ES2020"- ECMAScript 2020特性"ES2021"- ECMAScript 2021特性"DOM"- DOM类型"DOM.Iterable"- DOM迭代器"WebWorker"- Web Worker类型"node"- Node.js类型
10.3 @types组织与 DefinitelyTyped
Section titled “10.3 @types组织与 DefinitelyTyped”@types包
Section titled “@types包”DefinitelyTyped为npm包提供类型声明:
npm install --save-dev @types/node @types/react @types/express安装后,TypeScript自动识别这些类型。
@types搜索
Section titled “@types搜索”访问 https://github.com/DefinitelyTyped/DefinitelyTyped 查找类型包。
常用@types包:
@types/node- Node.js运行时@types/react- React@types/express- Express@types/lodash- Lodash@types/jest- Jest测试框架@types/mocha- Mocha测试框架
# 安装与主包版本匹配的@typesnpm install react@18npm install --save-dev @types/react@18声明包的格式
Section titled “声明包的格式”@types/├── node/│ ├── index.d.ts│ ├── ts3.6/│ │ └── index.d.ts│ └── package.json// @types/node/package.json{ "name": "@types/node", "version": "20.0.0", "types": "index.d.ts"}10.4 创建自定义类型声明
Section titled “10.4 创建自定义类型声明”为自己的代码创建声明
Section titled “为自己的代码创建声明”export function formatDate(date: Date): string { return date.toISOString().split("T")[0];}
// src/utils.d.tsexport function formatDate(date: Date): string;为外部JavaScript创建声明
Section titled “为外部JavaScript创建声明”function someLib(options) { return { result: options.value };}
// types/some-lib/index.d.tsdeclare module "some-lib" { interface Options { value: string; }
function someLib(options: Options): { result: string }; export = someLib;}declare module 语法
Section titled “declare module 语法”// 扩展已有模块declare module "existing-module" { export function newFunction(): void;}
// 扩展全局declare global { interface Array<T> { shuffle(): T[]; }}
// 无导入的扩展需要/// <reference types="..." />/// <reference types="extended-types" />命名空间声明
Section titled “命名空间声明”declare namespace MathUtils { function add(a: number, b: number): number; function subtract(a: number, b: number): number;
namespace Constants { const PI: number; const E: number; }}
// 使用MathUtils.add(1, 2);MathUtils.Constants.PI;10.5 声明合并(declaration merging)
Section titled “10.5 声明合并(declaration merging)”同名接口自动合并:
interface User { name: string;}
// user-extension.tsinterface User { email: string; age: number;}
// 合并后等价于interface User { name: string; email: string; age: number;}命名空间合并
Section titled “命名空间合并”namespace Validation { export interface StringValidator { isValid(s: string): boolean; }}
namespace Validation { export class EmailValidator implements StringValidator { isValid(s: string): boolean { return /@/.test(s); } }}类与命名空间合并
Section titled “类与命名空间合并”class User { name: string;}
namespace User { export function createGuest(): User { return { name: "Guest" }; }}
const guest = User.createGuest();| 类型 | 合并规则 |
|---|---|
| 接口 | 非函数成员必须唯一,函数成员重载 |
| 命名空间 | 导出成员合并 |
| 类 | 不能直接合并,但可以用mixin |
| 枚举 | 成员值可以覆盖 |
| 命名空间别名 | 不能合并 |
接口合并冲突
Section titled “接口合并冲突”interface A { method(x: number): number;}
interface A { method(x: string): string; // 重载签名}
interface B { prop: string;}
interface B { prop: number; // 错误!类型冲突}10.6 模块增强(Module Augmentation)
Section titled “10.6 模块增强(Module Augmentation)”扩展已有模块的功能:
// 原模块 express/index.d.tsdeclare module "express" { export interface Request { user?: User; }}
// 扩展// express-extensions.d.tsimport "express";
declare module "express" { export interface Request { sessionId?: string; }}// 为express添加自定义属性import "express";
declare module "express" { export interface Request { tenantId?: string; traceId?: string; }}
// 为mongoose添加静态方法import "mongoose";
declare module "mongoose" { interface Model<T> { findByAge(age: number): Promise<T[]>; }}// 扩展Arraydeclare global { interface Array<T> { first(): T | undefined; last(): T | undefined; }}
Array.prototype.first = function() { return this[0];};
Array.prototype.last = function() { return this[this.length - 1];};
export {};10.7 环境声明与环境模块
Section titled “10.7 环境声明与环境模块”使用declare声明在非TypeScript代码中存在的符号:
// 环境变量declare const process: { env: { NODE_ENV: string; [key: string]: string; };};
// 全局函数declare function setTimeout(callback: () => void, ms: number): number;
// 全局类declare class CustomEvent { detail: any; constructor(type: string); initCustomEvent(): void;}声明外部模块:
// 模块值declare module "my-custom-module" { export const VERSION: string; export function doSomething(): void; export class MyClass {}}
// 默认导出declare module "json" { export default JSON.parse;}triple-slash引用
Section titled “triple-slash引用”使用///引用类型:
/// <reference types="node" />/// <reference path="./local.d.ts" />// 使用示例/// <reference types="express" />
// 全局可以使用Express类型const app: Express = express();10.8 实际应用:迁移JS到TS
Section titled “10.8 实际应用:迁移JS到TS”策略一:先声明后实现
Section titled “策略一:先声明后实现”function add(a, b) { return a + b;}
function multiply(a, b) { return a * b;}
module.exports = { add, multiply };export function add(a: number, b: number): number;export function multiply(a: number, b: number): number;策略二:逐步添加类型
Section titled “策略二:逐步添加类型”// utils.ts(从utils.js复制并添加类型)export function add(a: number, b: number): number { return a + b;}
export function multiply(a: number, b: number): number { return a * b;}策略三:JSDoc注释
Section titled “策略三:JSDoc注释”/** * @param {number} a * @param {number} b * @returns {number} */function add(a, b) { return a + b;}TypeScript会自动从JSDoc推断类型(需要配置allowJs: true)。
tsconfig配置
Section titled “tsconfig配置”{ "compilerOptions": { "allowJs": true, "checkJs": true, "noEmit": true }}10.9 本章小结
Section titled “10.9 本章小结”本章学习了TypeScript的类型声明与声明文件:
-
.d.ts声明文件:
- 为JavaScript代码提供类型信息
- 放在项目目录或node_modules/@types中
-
内置类型声明:
- JavaScript标准库
- DOM类型
- 通过lib配置控制
-
@types与DefinitelyTyped:
- npm安装类型声明
- 匹配主包版本
-
自定义类型声明:
- declare module扩展外部模块
- declare global扩展全局
-
声明合并:
- 同名接口自动合并
- 命名空间可以合并
-
模块增强:
- import “module”后使用declare module扩展
- 添加自定义属性和方法
练习10.1:创建声明文件
Section titled “练习10.1:创建声明文件”为之前写的JavaScript函数创建对应的.d.ts声明文件。
练习10.2:安装和使用@types
Section titled “练习10.2:安装和使用@types”安装@types/lodash,使用其类型声明。
练习10.3:扩展第三方库
Section titled “练习10.3:扩展第三方库”为express的Request添加自定义属性。
练习10.4:声明合并
Section titled “练习10.4:声明合并”创建两个同名的interface,验证合并行为。
练习10.5:迁移策略
Section titled “练习10.5:迁移策略”将一个JavaScript模块迁移到TypeScript,使用JSDoc注释方式。
下一章我们将学习TypeScript的装饰器与元编程,掌握如何使用装饰器增强类和函数的行为。