13. 模块系统
13. 模块系统1. 概述TypeScript 模块系统基于 ECMAScript 模块ESM标准支持代码的组织、封装和复用。模块是 TypeScript 组织代码的基本单位每个文件都是一个独立的模块。┌─────────────────────────────────────────────────────────────┐ │ TypeScript 模块系统 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 模块导出 │ │ ├── 命名导出export const name value│ │ ├── 默认导出export default value│ │ ├── 批量导出export { name1, name2 }│ │ ├── 重命名导出export { name as alias }│ │ └── 类型导出export type { Type }│ │ │ │ 模块导入 │ │ ├── 命名导入import { name } from ./module│ │ ├── 默认导入import name from ./module│ │ ├── 混合导入import name, { other } from ./module│ │ ├── 命名空间导入import * as ns from ./module│ │ ├── 类型导入import type { Type } from ./module│ │ └── 动态导入const module await import(./module)│ │ │ │ 模块解析策略 │ │ ├── Classic传统 TypeScript 解析 │ │ ├── NodeNode.js 模块解析 │ │ └── Bundler打包器解析TypeScript5.0 │ │ │ └─────────────────────────────────────────────────────────────┘2. 导出Export2.1 命名导出// math.ts// 单独导出exportconstPI3.14159;exportconstE2.71828;exportfunctionadd(a:number,b:number):number{returnab;}exportclassCalculator{multiply(a:number,b:number):number{returna*b;}}// 批量导出constsubtract(a:number,b:number)a-b;constdivide(a:number,b:number)a/b;export{subtract,divide};// 重命名导出export{subtractassub,divideasdiv};2.2 默认导出// logger.ts// 单个默认导出constlogger{info:(msg:string)console.log([INFO]${msg}),error:(msg:string)console.error([ERROR]${msg}),warn:(msg:string)console.warn([WARN]${msg})};exportdefaultlogger;// 函数默认导出exportdefaultfunctiongreet(name:string):string{returnHello,${name}!;}// 类默认导出exportdefaultclassUser{constructor(publicname:string,publicage:number){}}2.3 类型导出// types.ts// 类型别名导出exporttypeUserIDstring|number;exporttypeStatuspending|active|inactive;// 接口导出exportinterfaceUser{id:UserID;name:string;email:string;status:Status;}// 类型和值混合导出exportconstDEFAULT_USER:User{id:guest,name:Guest,email:guestexample.com,status:active};// 使用 export type 明确导出类型exporttype{UserasUserType};3. 导入Import3.1 命名导入// app.ts// 导入命名导出import{PI,E,add,Calculator}from./math;console.log(PI);// 3.14159console.log(add(5,3));// 8constcalcnewCalculator();console.log(calc.multiply(4,5));// 20// 导入重命名的导出import{sub,div}from./math;console.log(sub(10,3));// 7console.log(div(10,2));// 5// 导入时重命名import{addasaddNumbers}from./math;console.log(addNumbers(1,2));3.2 默认导入// 导入默认导出importloggerfrom./logger;importgreetfrom./logger;importUserfrom./logger;logger.info(Application started);console.log(greet(Alice));constusernewUser(Bob,30);// 默认导入可以任意命名importmyLoggerfrom./logger;myLogger.info(Using custom name);3.3 混合导入// 同时导入默认和命名导出importlogger,{LogLevel,formatMessage}from./logger;// 导入默认和所有命名导出importlogger,*asloggingfrom./logger;3.4 命名空间导入// 将所有导出导入为一个对象import*asMathUtilsfrom./math;console.log(MathUtils.PI);// 3.14159console.log(MathUtils.add(5,3));// 8console.log(MathUtils.sub(10,4));// 6constcalcnewMathUtils.Calculator();console.log(calc.multiply(3,4));// 123.5 类型导入// 只导入类型不导入值importtype{User,Status}from./types;// 混合导入值和类型import{DEFAULT_USER,typeUser}from./types;constuser:User{id:1,name:Alice,email:aliceexample.com,status:active};console.log(DEFAULT_USER);4. 动态导入4.1 基本用法// 动态导入返回 PromiseasyncfunctionloadModule(){constmathawaitimport(./math);console.log(math.add(5,3));}// 条件加载asyncfunctionloadFeature(featureName:string){if(featureNamechart){constchartawaitimport(./chart);chart.render();}elseif(featureNametable){consttableawaitimport(./table);table.display();}}// 动态导入类型asyncfunctionloadTypes(){constmoduleawaitimport(./types);typeUsermodule.User;}4.2 React 懒加载// React 组件懒加载import{lazy,Suspense}fromreact;constDashboardlazy(()import(./Dashboard));constProfilelazy(()import(./Profile));functionApp(){return(Suspense fallback{divLoading.../div}Dashboard//Suspense);}5. 模块解析5.1 相对路径 vs 绝对路径// 相对路径相对于当前文件import{helper}from./utils/helper;import{config}from../config;// 绝对路径基于 baseUrlimport{helper}fromutils/helper;import{config}fromconfig;// 路径映射paths 配置import{UserService}fromservices/user;import{formatDate}fromutils/date;5.2 路径映射配置// tsconfig.json{compilerOptions:{baseUrl:.,paths:{/*:[src/*],components/*:[src/components/*],utils/*:[src/utils/*],services/*:[src/services/*]}}}// 使用路径映射importButtonfromcomponents/Button;import{formatDate}fromutils/date;import{UserService}fromservices/UserService;6. 命名空间Namespace虽然命名空间在模块化时代已经不推荐使用但了解其语法有助于阅读旧代码。// namespace.tsnamespaceValidation{exportinterfaceStringValidator{isAcceptable(s:string):boolean;}exportclassEmailValidatorimplementsStringValidator{isAcceptable(s:string):boolean{return//.test(s);}}exportclassPhoneValidatorimplementsStringValidator{isAcceptable(s:string):boolean{return/\d{11}/.test(s);}}}// 使用命名空间constemailValidatornewValidation.EmailValidator();console.log(emailValidator.isAcceptable(testexample.com));// true// 命名空间可以跨文件合并// 推荐使用模块代替命名空间7. 三斜线指令三斜线指令用于在旧代码中引用依赖现代 TypeScript 项目推荐使用 ES 模块。/// reference path../types/global.d.ts //// reference typesnode //// reference libdom /// 使用全局类型constelement:HTMLElementdocument.getElementById(app);8. 完整示例模块化应用// 1. 类型定义模块 // types/user.tsexportinterfaceUser{id:number;name:string;email:string;createdAt:Date;}exporttypeUserCreateInputOmitUser,id|createdAt;exporttypeUserUpdateInputPartialOmitUser,id;// 2. 服务模块 // services/userService.tsimporttype{User,UserCreateInput,UserUpdateInput}from../types/user;exportclassUserService{privateusers:Mapnumber,UsernewMap();privatenextId:number1;asynccreate(data:UserCreateInput):PromiseUser{constuser:User{id:this.nextId,...data,createdAt:newDate()};this.users.set(user.id,user);returnuser;}asyncfindById(id:number):PromiseUser|null{returnthis.users.get(id)||null;}asyncfindAll():PromiseUser[]{returnArray.from(this.users.values());}asyncupdate(id:number,data:UserUpdateInput):PromiseUser|null{constuserthis.users.get(id);if(!user)returnnull;constupdated{...user,...data};this.users.set(id,updated);returnupdated;}asyncdelete(id:number):Promiseboolean{returnthis.users.delete(id);}}exportconstuserServicenewUserService();// 3. 工具模块 // utils/validation.tsexportfunctionvalidateEmail(email:string):boolean{constemailRegex/^[^\s][^\s]\.[^\s]$/;returnemailRegex.test(email);}exportfunctionvalidateName(name:string):boolean{returnname.length2name.length50;}exportfunctionformatDate(date:Date):string{returndate.toLocaleDateString(zh-CN);}// 4. 控制器模块 // controllers/userController.tsimport{userService}from../services/userService;import{validateEmail,validateName,formatDate}from../utils/validation;importtype{UserCreateInput}from../types/user;exportclassUserController{asynccreateUser(req:{body:UserCreateInput}){const{name,email}req.body;if(!validateName(name)){return{success:false,error:Invalid name};}if(!validateEmail(email)){return{success:false,error:Invalid email};}constuserawaituserService.create(req.body);return{success:true,data:user};}asyncgetUser(id:number){constuserawaituserService.findById(id);if(!user){return{success:false,error:User not found};}return{success:true,data:{...user,formattedDate:formatDate(user.createdAt)}};}}// 5. 主入口模块 // index.tsimport{UserController}from./controllers/userController;import{userService}from./services/userService;asyncfunctionmain(){constcontrollernewUserController();// 创建用户constcreateResultawaitcontroller.createUser({body:{name:Alice,email:aliceexample.com}});console.log(Create result:,createResult);// 获取用户if(createResult.successcreateResult.data){constgetUserResultawaitcontroller.getUser(createResult.data.id);console.log(Get user result:,getUserResult);}// 列出所有用户constusersawaituserService.findAll();console.log(All users:,users);}main().catch(console.error);9. 模块解析策略策略说明适用场景classicTypeScript 原始解析方式已废弃不推荐nodeNode.js 模块解析Node.js 项目bundler打包器解析TS 5.0前端项目Vite、WebpacknodenextNode.js 最新解析Node.js ESM 项目// tsconfig.json{compilerOptions:{module:ESNext,moduleResolution:bundler,esModuleInterop:true,allowSyntheticDefaultImports:true,resolveJsonModule:true}}10. 总结导出方式语法导入语法命名导出export const x 1import { x }默认导出export default ximport x批量导出export { a, b }import { a, b }重命名导出export { a as b }import { b }类型导出export type T ...import type { T }命名空间导入import * as nsns.x动态导入await import()Promise 异步