尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

TypeScript总结:19、综合案例

TypeScript总结:19、综合案例 TypeScript 综合案例任务管理器本文通过一个简单的任务管理器案例把前面学习的枚举、接口、类、封装、泛型和严格类型检查串联起来。一、案例目标实现以下功能创建任务。查询任务。完成任务。保存任务列表。使用泛型编写通用仓库。使用接口约束数据结构。使用类封装业务逻辑。二、项目结构为了方便学习先把代码写在一个文件中ts-task/ ├── src/ │ └── index.ts ├── dist/ ├── package.json └── tsconfig.json实际项目中可以进一步拆分为src/ ├── models/ │ └── task.ts ├── repositories/ │ └── memory-repository.ts ├── services/ │ └── task-service.ts └── index.ts三、定义任务状态任务状态是有限的几个固定值适合使用字符串枚举enumTaskStatus{Todotodo,Doingdoing,Donedone}使用枚举后任务状态不会被随意写成任意字符串conststatus:TaskStatusTaskStatus.Todo;四、定义任务接口接口用于描述任务对象的结构interfaceTask{readonlyid:number;title:string;status:TaskStatus;}id是任务唯一标识创建后不应该被修改所以使用readonly。五、定义泛型仓库接口不同业务可能需要保存不同类型的数据因此仓库可以使用泛型interfaceRepositoryTextends{readonlyid:number}{add(item:T):void;getById(id:number):T|undefined;list():T[];}这里的泛型约束表示传入仓库的数据必须具有只读的id数字属性。六、实现内存仓库使用类实现仓库接口classMemoryRepositoryTextends{readonlyid:number}implementsRepositoryT{privateitems:T[][];add(item:T):void{this.items.push(item);}getById(id:number):T|undefined{returnthis.items.find((item)item.idid);}list():T[]{return[...this.items];}}这里体现了几个知识点private外部不能直接修改内部数组。泛型T仓库可以保存不同类型的数据。implements类必须实现接口中声明的方法。T | undefined查询不到数据时明确表示可能没有结果。七、封装任务服务任务服务负责处理任务相关业务classTaskService{constructor(privatereadonlyrepository:RepositoryTask){}create(title:string):Task{consttask:Task{id:Date.now(),title,status:TaskStatus.Todo};this.repository.add(task);returntask;}complete(id:number):boolean{consttaskthis.repository.getById(id);if(!task){returnfalse;}task.statusTaskStatus.Done;returntrue;}list():Task[]{returnthis.repository.list();}}TaskService不需要知道任务具体保存在哪里它只依赖RepositoryTask接口。这种设计方便以后把内存仓库替换成数据库仓库或接口仓库。八、运行案例constrepositorynewMemoryRepositoryTask();consttaskServicenewTaskService(repository);consttasktaskService.create(学习 TypeScript);console.log(创建任务,task);taskService.complete(task.id);console.log(任务列表,taskService.list());预期结果类似创建任务 { id: 1690000000000, title: 学习 TypeScript, status: todo } 任务列表 [ { id: 1690000000000, title: 学习 TypeScript, status: done } ]Date.now()的具体值会根据运行时间变化上面的数字只是示例。九、处理未知输入如果任务状态来自用户输入或网络请求不能直接断言为TaskStatus应该先校验functionisTaskStatus(value:unknown):valueisTaskStatus{return(valueTaskStatus.Todo||valueTaskStatus.Doing||valueTaskStatus.Done);}functionparseTaskStatus(value:unknown):TaskStatus{if(isTaskStatus(value)){returnvalue;}returnTaskStatus.Todo;}这里使用了unknown表示外部传入的数据类型未知。类型谓词value is TaskStatus告诉编译器检查成功后值就是任务状态。默认值输入不合法时回退到Todo。十、完整 index.ts下面的代码可以直接保存为src/index.tsenumTaskStatus{Todotodo,Doingdoing,Donedone}interfaceTask{readonlyid:number;title:string;status:TaskStatus;}interfaceRepositoryTextends{readonlyid:number}{add(item:T):void;getById(id:number):T|undefined;list():T[];}classMemoryRepositoryTextends{readonlyid:number}implementsRepositoryT{privateitems:T[][];add(item:T):void{this.items.push(item);}getById(id:number):T|undefined{returnthis.items.find((item)item.idid);}list():T[]{return[...this.items];}}classTaskService{constructor(privatereadonlyrepository:RepositoryTask){}create(title:string):Task{consttask:Task{id:Date.now(),title,status:TaskStatus.Todo};this.repository.add(task);returntask;}complete(id:number):boolean{consttaskthis.repository.getById(id);if(!task){returnfalse;}task.statusTaskStatus.Done;returntrue;}list():Task[]{returnthis.repository.list();}}constrepositorynewMemoryRepositoryTask();consttaskServicenewTaskService(repository);consttasktaskService.create(学习 TypeScript);console.log(创建任务,task);taskService.complete(task.id);console.log(任务列表,taskService.list());十一、配置 tsconfig.json{compilerOptions:{target:ES2015,module:CommonJS,rootDir:src,outDir:dist,strict:true,noEmitOnError:true},include:[src/**/*.ts],exclude:[node_modules,dist]}十二、运行项目初始化项目并安装 TypeScriptnpminit-ynpminstall--save-dev typescript编译npx tsc运行编译结果nodedist/index.js也可以在package.json中添加{scripts:{build:tsc,start:node dist/index.js,type-check:tsc --noEmit}}十三、案例中的知识点对应关系知识点案例中的使用位置枚举TaskStatus接口Task、RepositoryTreadonly任务id类MemoryRepository、TaskServiceprivate仓库内部的items泛型RepositoryT和MemoryRepositoryT泛型约束T extends { readonly id: number }unknown外部任务状态校验严格检查strict: true十四、可以继续扩展的功能在这个案例上可以继续添加删除任务。修改任务标题。按状态筛选任务。使用抽象类定义不同类型的仓库。使用 Webpack 打包项目。把内存仓库替换成数据库或 HTTP API。为任务服务增加单元测试。十五、本章小结接口负责描述结构。枚举负责约束有限状态。类负责封装数据和业务行为。泛型负责编写可复用的类型安全仓库。unknown和类型谓词负责处理外部不可信输入。tsconfig.json负责统一项目编译配置。
返回列表