[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-config":3,"article-ts":22,"comments-ts":43,"footer-socials":44},{"site_title":4,"site_subtitle":5,"home_intro":6,"avatar":7,"seo_keywords":8,"seo_description":9,"site_subtitle_highlight":10,"home_description":11,"about_name":12,"about_intro":13,"code_comment_2":14,"code_log":15,"code_skills":16,"code_goal":17,"code_comment_1":18,"meteor_density":19,"meteor_max_count":19,"meteor_enabled":20,"meteor_speed":21},"ShineGoldYao","架构代码，","全栈开发者 \u002F 开源爱好者 \u002F 技术探索者","https:\u002F\u002Fforuda.gitee.com\u002Favatar\u002F1762402862010015318\u002F16382196_yaoxingjin_1762402861.png!avatar200","ShiGoldYao,技术博客,全栈开发","专注于前沿技术分享与开源项目展示的个人技术博客","书写未来。","大家好，我是 ShiGoldYao。一名全栈学习与技术爱好者。在这里，我分享关于现代 Web 开发、技术框架学习记录以及极客生活的深度思考。","ShiGoldYao","我是一名全栈技术学习者，对构建高性能、可扩展的现代 Web 应用充满热情。过去一年里，我从初识互联网前后端开发，到逐步沉淀技术体系，始终保持着对前端、后端与工程化的持续探索。\n\n我坚信 “代码如诗”。除了日常学习与项目实践，我也会花大量时间关注开源社区，尝试摸索 WebAssembly、Rust 等前沿技术在浏览器端的更多可能，不断挑战性能与体验的边界。\n\n生活里的我并不只有代码：闲暇时会打打永劫无间，享受博弈与操作的快感；也喜欢打乒乓球，在运动中放松自己；当然，最幸福的时光，还是和女朋友一起慢慢生活、认真恋爱。","\u002F\u002F 🚀 开启学习之旅","","Vue , TypeScript , Nest , Mysql","成为优秀的前端全栈工程师","\u002F\u002F 欢迎来到我的技术世界","3","true","5",{"id":23,"title":24,"slug":25,"coverUrl":15,"summary":15,"content":26,"htmlContent":27,"categoryId":28,"viewCount":29,"likeCount":30,"isTop":30,"isPublish":31,"seoKeywords":15,"seoDescription":15,"publishTime":32,"createTime":33,"updateTime":34,"deleteTime":35,"category":36,"tags":42},"6","1 - Ts 简介","ts","# 1 - Ts 简介\n\n​\t由微软开发，是基于js的扩展语言。包含了js的所有内容，是js的超集。增加了静态类型检查，接口，泛型等很多现代开发特性，适合大型项目。需要编译为js，然后运行。\n\n## 1.1 js困扰（为什么用ts）\n\n​\t1.不清楚的数据类型\n\n```javascript\nlet welcome = 'hello'\nwelcome() \u002F\u002F报错 这不是一个函数，当代码多的时候容易出错\n```\n\n​\t2.有漏洞的逻辑\n\n```javascript\nconst str = Date.now() % 2 ? '奇数' : '偶数'\nif(str !== '奇数'){\n    alert('hello')\n}\nelse if(str === '偶数'){\n    alert('world')\n}   \u002F\u002F 错误的逻辑，但是js不会提示\n```\n\n​\t3.访问不存在的属性\n\n```javascript\nconst obj = { width : 10, height : 20}\nconst area = obj.width * obj.heigth\n```\n\n​\t4.低级的拼写错误\n\n```javascript\nconst message = 'hello wordl'\nmessage.toUperCase  \u002F\u002F转为大写的时候\n```\n\n​\t静态类型检查：在代码运行前进行检查，发现代码的错误以及不合理的地方。\n\n# 2 - TypeScript的编译\n\n## 2.1 命令行编译\n\n​\t要把 .ts 文件编译为 .js 文件，需要配置 ts 的编译环境\n\n\t1. 创建一个 demo.ts 文件\n\n```typescript\nconst person = {\n    name:'张三',\n    age:18\n}\nconsole.log(`我叫${person.name} 我今年${person.age}岁了`);\n```\n\n\t2. 全局安装 TypeScript    **npm i typescript -g**\n \t3. 使用命令行编译 .ts 文件   **tsc demo.ts**\n\n## 2.2 自动化编译\n\n 1. 创建 ts 编译控制文件     **tsc --init**\n\n    * 工程中会出现 tsconfig.json 配置文件 包含很多编译时的配置\n\n    * 取消杂文件生成，只转成 .js 文件\n\n      ```typescript\n      \u002F\u002F Other Outputs\n      \u002F\u002F \"sourceMap\": true,\n      \u002F\u002F \"declaration\": true,\n      \u002F\u002F \"declarationMap\": true,   将这三项注释掉\n      ```\n\n    * 默认编译的 js 版本 ，可以手动调整\n\n      ```typescript\n         \"target\": \"esnext\",\n      ```\n\n 2. 监视目录中 .ts 文件变化    **tsc --watch**        或者  **tsc --noEmitOnError --watch** \n\n 3. 小优化，当编译出错时，不生成 .js 文件\n\n    ```typescript\n    \"compilerOptions\": {\n        \u002F\u002F File Layout\n        \u002F\u002F \"rootDir\": \".\u002Fsrc\",\n        \u002F\u002F \"outDir\": \".\u002Fdist\",\n    \"noEmitOnError\": true,    \u002F\u002F 添加这一项即可\n    ```\n\n# 3 - 类型声明\n\n * 使用 : 来对**变量**或者**函数形参**，进行类型声明\n\n   ```typescript\n   \u002F\u002F 使用 : 来对变量或函数形参，进行类型声明\n   let a : string  \u002F\u002Fa 只能存字符串\n   let b : number \u002F\u002Fb 只能存数值类型\n   let c : boolean \u002F\u002Fc 只能布尔类型\n   let d : 'hee' \u002F\u002Fd 只能为'dee'\n   d = 'hee'  \n   a ='hello'\n   \u002F\u002F a = 100 \n   b = 666\n   \u002F\u002F b = 'heep'\n   c = true\n   \n   \u002F\u002F 参数x和y必须是数字，返回值也必须是数字\n   function demo (x : number,y : number) : number{\n       return x + y\n   }\n   \n   demo(100,200)\n   \u002F\u002F demo(100.'220')\n   \u002F\u002F 对参数的个数也严格限制\n   \u002F\u002F demo(100,200,300)\n   \u002F\u002F demo(100)\n   \n   ```\n\n# 4 - 类型总览\n\n## 4.1 js数据类型\n\n* string\n* number\n* boolean\n* null\n* undefined\n* bigint\n* symbol\n* object  :    Array ,Function,Date,Error等......\n\n## 4.2 ts数据类型\n\nts新增\n\n* any\n* unknown\n* never\n* void\n* tuple\n* enum\n* type(自定义)\n* interface(自定义)\n\n注意：在js中这些内置构造函数 ,Number,String,Boolean，用于创建对应的包装对象，很少使用。在ts中也是同理，在ts进行类型声明的时候，都是小写的number，string，boolean\n\n```typescript\nlet str1: string\nstr1 = 'hello'\n\u002F\u002F str1 = new String('hello')     \u002F\u002F报错\n\nlet str2: String\nstr2 = 'hello'\nstr2 = new String('hello')\nconsole.log(typeof str1);  \u002F\u002F string\nconsole.log(typeof str2);  \u002F\u002F objec\n\n```\n\n注意：原始类型vs包装对象\n\n* 原始类型：number，string，boolen，在js中是简单数据类型，在内存中占用少，处理速度快\n* 包装对象：Number对象，String对象，是复杂类型，占用更多内存\n* 自动装箱：js在必要是会自动将原始类型包装成对象，以便于方法的调用\n\n```typescript\n\u002F\u002F 原始类型字符串\nlet str = 'hello'\n\u002F\u002F 当访问str.length时，js引擎做了以下工作\nlet size = (function(){\n    \u002F\u002F 1.自动装箱：创建一个临时的String对象包装原始字符串\n    let tempStringObject = new String(str)\n    \u002F\u002F 2.访问String对象的length属性\n    let lengthValue = = tempStringObject.length\n    \u002F\u002F 3.销毁临时对象，返回长度值\n    return lengthValue\n})()\nconsole.log(size);  \u002F\u002F5\n\n```\n\n# 5 - Ts 数据类型\n\n## 5.1 any 任意类型 \n\n​\t任意类型 ，一旦将变量类型限制为any，那就意味着放弃了对该变量的类型检查\n\n```typescript\nlet a : any  \u002F\u002F 无报错 可以为任意类型\na = 100\na = 'hello'\na = true\n\nlet b    \u002F\u002F 不写数据类型 默认any\nb = 200\nb ='hellp'\nb =false\n\u002F\u002F 注意 ： any类型的变量，可以赋值给任意类型的变量\nlet c : any\nc =9\nlet x : string\nx =c  \u002F\u002F无警告\n```\n\n\n\n## 5.2 unknown 未知类型\n\n​\t未知类型，适用于起初不知道不确定的具体类型，需要后期才能确定\n\n* 可以理解为一个类型安全的 any\n\n  ```typescript\n  let a : unknown\n  a = 100\n  a ='hello'\n  a = false\n  \u002F\u002F 设置x的类型为string\n  let x : string\n  x = a   \u002F\u002F 警告 不能将类型unknown 分配给 string\n  \n  ```\n\n  \n\n* unknown 会强制开发者再使用之前进行类型检查，从而提供更强的类型安全性\n\n  ```typescript\n  \u002F\u002F 设置b为unknown\n  let b : unknown\n  b = 'hello'\n  let x : string\n  \u002F\u002F 第一种方式：加类型判断\n   if(typeof b === 'string'){\n       x = b\n    console.log(x);\n       }\n  \u002F\u002F 第二种方式：加断言\n  x = b as string\n  \n  x = \u003Cstring>b\n  ```\n\n  \n\n* 读取 any 类型数据的任何属性都不会报错，而 unknown 正好相反\n\n  ```typescript\n  let str1 : string\n  str1 = 'hello'\n  str1.toUpperCase()  \u002F\u002F 无警告\n  \n  let str2 : any\n  str2 = 'hello'\n  str2.toUpperCase()  \u002F\u002F无警告\n  \n  let str3 : unknown\n  str3 = 'hello';\n  \u002F\u002F str3.toUpperCase()  \u002F\u002F警告 str3的类型未知\n  \n  \u002F\u002F 使用强制断言指定str3的类型为string\n  (str3 as string).toUpperCase()\n  ```\n\n## 5.3 never 任何都不是\n\n​\t任何值都不是，即不能有值。如：undefined，null，''，0,都不行\n\n * 几乎不用 never 类型 去直接限制变量，无意义\n\n * never 也可用于限制函数的返回值\n\n   ```typescript\n   function throwError(str : string) : never{\n       throw new Error(\"程序异常退出\" + str); \u002F\u002F 截断函数 无法继续向下执行\n       \u002F\u002F 不写return 默认返回undefined  \n       \n   }\n   ```\n\n* never 一般是 ts 主动推断出来的\n\n  ```typescript\n  let a : string\n  a = 'hello'\n  if(typeof a === 'string'){\n      console.log(a.toUpperCase);\n      \n  }\n  \u002F\u002F 这个逻辑无法到达\n  else{\n      console.log(a); \u002F\u002F 会提示 let a : never\n      \n  }\n  ```\n\n## 5.4 void 空\n\n​\t 空，即函数不返回任何值(undefined除外)，调用者也不应该用其返回值进行操作(无法使用返回的undefined)\n\n * void 通常用于返回值声明\n\n   ```typescript\n   function logMessage (msg: string):void{\n       console.log(msg);\n   }\n   logMessage('你好')\n   ```\n\n   注意：没有写 return 指定函数返回值，所以函数是没有显示返回值的，但会有隐式返回值，即undefined，虽然函数返回类型为 void，但可以接受 undefined，它是 void 可以接受的一种空\n\n* 三种方式都可以接受\n\n  ```typescript\n  function logMessage (msg: string):void{\n      console.log(msg);\n  }\n  \n  function logMessage1 (msg: string):void{\n      console.log(msg);\n      return\n  }\n  function logMessage2 (msg: string):void{\n      console.log(msg);\n      return undefined\n  }\n  ```\n\n* 限制函数返回值时，undefined 和 void 有区别，void 不能用其返回值操作，undefined 可以\n\n  ```typescript\n  function logMessage (msg: string):void{\n      console.log(msg);\n  }\n  let result = logMessage('你好')\n  if(result){\n      console.log('不能使用返回值');\n  }\n  \n  function logMessage1 (msg: string):undefined{\n      console.log(msg);\n  }\n  let result1 = logMessage1('jja')\n  if(result1){\n      console.log('可以使用'); \n  }\n  ```\n\n\n## 5.5 object和Object 对象\n\n### \t5.5.1 object\n\n​\t所有非原始类型，可存储：对象，数组，函数等，限制宽泛，使用较少\n\n```typescript\nlet a:object\n\n\u002F\u002F 非原始类型赋给a，均可以\na = {}\na = {name:'张三'}\na = [1,2,5,6]\na = function(){}\na = new String('123')\nclass person{}\na = new person()\n\n\u002F\u002F 以下代码，是将原始类型赋给a，不可以\n\u002F\u002F a = 1\n\u002F\u002F a =true\n\u002F\u002F a = '你好'\n\u002F\u002F a = null\n\u002F\u002F a = undefined\n\n```\n\n\n\n### \t5.5.2 Object\n\n​\t所有可以调用Object方法的类型，除了null 和 underfunded 都可以存，限制宽泛，使用较少\n\n```typescript\nlet b:Object\nb = {}\nb = {name:'张三'}\nb = [1,2,5,6]\nb = function(){}\nb = new String('123')\nclass person1{}\nb = new person()\nb = 1\nb =true\nb = '你好'\n\u002F\u002F 除了null 和 undefined\n\n\u002F\u002F b = null\n\u002F\u002F b = undefined\n\n```\n\n​\t\n\n### \t5.5.3 对象一般声明\n\n```typescript\n\u002F\u002F 限制一般对象\nlet person2 : {name: string,age: number}\n\u002F\u002F age?为可选属性，可有可无\n\n\u002F\u002F 可以分号分割\nlet person3 : {name: string;age?: number}\n\u002F\u002F 可以换号分割\nlet person4 : {\n    name: string\n    age?: number}\n\nperson2 = {name:'李四',age:18}\nperson3 = {name: '王五',age:22}\nperson3 = {name: '赵六'}\n\n\u002F\u002F 不能添加没有属性\n\u002F\u002F person3 = {name:'你好',gender:'男'}\n\n\u002F\u002F 索引签名，让对象具有任意数量的属性\n\n\u002F\u002F person5，限制必须有name属性 可选age 属性，可以添加任意数量为任何值的属性\nlet person5:{\n    name:string\n    age?:number\n    [key: string]: any   \u002F\u002Fkey 索引签名，可以换替他的\n}\nperson5 = {\n    name:'johg',\n    age:22,\n    gender:'男',\n    ate:function(){\n        console.log('555');     \n    }\n}\n\n\n```\n\n\n\n### \t5.5.4 函数一般声明\n\n```typescript\n\u002F\u002F 声明函数类型\nlet count : (a: number,b: number) => number\ncount = function(x,y) {\n    return x + y\n}\n```\n\n注意：ts中 => 在函数类型声明时表示函数类型，描述其参数类型和返回类型\n\n​\t\t\tjs中， => 是一种定义函数的语法，是具体函数的体现\n\n​\t\t\t函数类型声明还可以使用，接口，自定义方式类型声明\n\n### \t5.5.5 数组一般声明\n\n```typescript\n\u002F\u002F 声明数组\nlet arr1: string[]\nlet arr2: Array\u003Cstring>  \u002F\u002F 属于泛型\n\narr1 = ['a,b,b,']\narr2 = ['heelo','dwa']\n\n```\n\n\n\n## 5.6 tuple 元组\n\n​\t是一种特殊的数据类型，可以存储固定数量的元素，并且每个元素的类型是已知的且可以不同。精确描述一组值的类型，？表示可选元素\n\n```typescript\n\u002F\u002F tuple\nlet arr1: [string,number]\nlet arr2: [number,boolean?]\nlet arr3: [number,...string[]]\n\narr1 = ['hello',123]\narr2 = [200,true]\narr2 = [200]\narr3 = [100,'heelo','world']   \u002F\u002F 第一个为数值，后面的元素可以任意数量\narr3 = [100]   \n\n```\n\n\n\n## 5.7 enum 枚举\n\n​\t枚举可以定义一组命名常量,能增强代码的可读性,让代码更好维护,被枚举的常量为**连续且相关的一组值**\n\n### \t5.7.1数字枚举\n\n​\t成员的值会**自动递增**,且具备反向映衬的特点,即key,value互换,可以通过值来获取对应的枚举成员名称\n\n```typescript\n\n\u002F\u002F 定义一个描述上下左右方向的枚举\nenum Direction {\n    Up,\n    Down,\n    Left,\n    Right\n}\nconsole.log(Direction);\n\n\u002F\u002F 反向映射\nconsole.log(Direction.Up);\u002F\u002F 打印为0,0代表Up\nconsole.log(Direction.[0]);  \u002F\u002F 不行\nDirection.Up = 'shang'  \u002F\u002F 报错  枚举中的属性是只读的\n\n\u002F\u002F 增强代码的可读性,正确性,防止在写方向的时候 down 写成 donw\nfunction walk(data:Direction){\n    if(data === Direction.Up){\n        console.log('向上走');\n    }\n    else if(data === Direction.Right){\n        console.log('向右走');\n    }\n    else if(data === Direction.Left){\n        console.log('想左走');\n        \n    }\n    else if(data === Direction.Down){\n        console.log('向下走');\n        \n    }\n    else{\n        console.log('未知方向');\n        \n    }\n}\nwalk(Direction.Down)\n```\n\n注意;也可以指定枚举成员的初始值,其后的成员值会自动递增\n\n```typescript\nenum Direction {\n    Up = 6,\n    Down,\n    Left,\n    Right\n}\nconsole.log(Direction);\n\n```\n\n### 5.7.2 字符串枚举\n\n​\t枚举成员的值为字符串\n\n```typescript\nenum Direction{\n    Up ='up',\n    Down = 'down',\n    Right = 'right',\n    Left = 'left'\n}\nlet dir : Direction = Direction.Up\nconsole.log(dir);  \u002F\u002F 打印为up\n\n```\n\n### 5.7.3 常量枚举\n\n​\t特殊枚举类型,使用 const 关键字定义,在编译时会被内联,避免生成一些额外的代码\n\n```typescript\nenum Direction{\n    Up ='up',\n    Down = 'down',\n    Right = 'right',\n    Left = 'left'\n}\nlet dir : Direction = Direction.Up\nconsole.log(dir);  \u002F\u002F 打印为up\n```\n\n```javascript\n\u002F\u002F 只用Direction.Up,但会生成好多无用代码\nvar Direction;\n(function (Direction) {\n    Direction[\"Up\"] = \"up\";\n    Direction[\"Down\"] = \"down\";\n    Direction[\"Right\"] = \"right\";\n    Direction[\"Left\"] = \"left\";\n})(Direction || (Direction = {}));\nlet dir = Direction.Up;\nconsole.log(dir); \u002F\u002F 打印为up\n\n```\n\n注意:有点问题,后续解决\n\n\n\n## 5.8 type\n\n​\ttype可以为任意类型创建别名,让代码更简洁,可读性更强,同时方便进行类型复用和扩展.\n\n### \t5.8.1 基本用法\n\n```typescript\n\u002F\u002F type 名字 = 类型\ntype num = number\nlet price:num\nprice = 100\n```\n\n\n\n### \t5.8.2 联合类型\n\n```typescript\n\u002F\u002F 表示几种不同类型之一\ntype Status = number | string\ntype Gender = '男' | '女'\nfunction printStatus(status: Status){\n    console.log(status);\n}\nfunction logGender(str:Gender){\n    console.log(str);\n    \n}\n\nprintStatus(404)\nprintStatus('404')\nlogGender('女')\nlogGender('男')\n```\n\n\n\n### \t5.8.3 交叉类型\n\n```typescript\n\u002F\u002F 交叉类型,将多个类型和为一个类型.合并后的类型将拥有所有被合并类型的成员.通常用于对象类型\n\u002F\u002F面积\ntype Area = {\n    height: number\n    width: number\n}\n\n\u002F\u002F地址\ntype Address ={\n    num: number \u002F\u002F 楼号\n    cell: number \u002F\u002F单元号\n    room: string \u002F\u002F房间号\n}\n\u002F\u002F 定义House,且House是Area和Address促成的交叉类型\ntype House = Area & Address\n\nconst house: House = {\n    height:180,\n    width:200,\n    num:6,\n    cell:7,\n    room:'703'\n}\nconsole.log(house);\n\n```\n\n### \t5.8.4 特殊情况\n\n​\t\ttype定义函数且返回值为void,ts 不会严格要求函数返回值为空\n\n```typescript\n\u002F\u002F 正常\nfunction demo():void{\n    \u002F\u002F 返回underfined合法\n    return undefined\n    \u002F\u002F 以下不合法\n    \u002F\u002F return 100\n    \u002F\u002F return false\n    \u002F\u002F return null\n    \u002F\u002F return []\n}\ndemo()\n\n\u002F\u002F 特殊  使用type限制函数返回值为void 时,ts并不会严格限制要求函数返回为空\n\n\u002F\u002F 定义一个函数 返回值为void\ntype LogFunc = () => void\n\u002F\u002F 是一个类型,类似下面这种写\n\u002F\u002F const num: number\n\nconst f1: LogFunc = () =>{\n    return 100 \u002F\u002F 合法\n}\nconst f2: LogFunc = () => 200   \u002F\u002F合法  允许返回非空值\n\nconst f3: LogFunc = function (){\n    return 300  \u002F\u002F 允许返回非空值\n}\n\n\u002F\u002F 为什么\nconst scr = [1,2,3]\nconst dst = [0]\nscr.forEach((el) => dst.push(el))   \u002F\u002F数组方法foreach的返回类型为void\n```\n\n**注意：即 当我们定义函数类型限制返回值为void时，好用，返回值只能为undefined，但是当用type定义函数且函数返回值为void时，就不起作用了，但是也不能去使用后来强制返回的值  (毕竟返回值还是void)**\n\n\n\n# 5.9 interface 接口\n\n​\t是一种定义结构的方式，主要作用是为：类，对象，函数等规定一种契约，这样可以确保代码的一致性和类型安全，只能定义格式，不能包含具体实现。\n\n​\t**何时使用:**\n\n* 定义对象的格式：描述数据模型，api响应格式，配置对象\n* 类的契约：规定一个类需要哪些属性和方法\n* 自动合并：一般用于扩展第三方库的类型，独特特性在大项目中使用\n\n## \t5.9.1 定义类结构\n\n​\t用 implements 实现规定\n\n```typescript\n\u002F\u002F 接口类\n\u002F\u002F PersonInterface接口,用于限制Person类的格式\ninterface PersonInterface {\n    name: string\n    age: number\n    speak(n: number): void\n}\n\u002F\u002F 定义一个类 Person 实现 PersonInterface 接口\nclass Person implements PersonInterface {\n    constructor(\n        public name: string,\n        public age: number\n    ){}\n    \u002F\u002F 实现接口的方法\n    speak(n: number): void {\n        for(let i = 0;i\u003C n;i++){\n            console.log(`你好，我叫${this.name}，我的年龄是${this.age}`);\n        }\n    }\n}\n\u002F\u002F 创建一个 Person 类的实例 p1，传入name,age\nconst p1 = new Person('tom',18)\np1.speak(3)\n```\n\n\n\n## \t5.9.2 定义对象结构\n\n```typescript\ninterface UserInterface {\n    name: string\n    readonly gender: string\n    age?: number\n    run:(n: number) => void\n}\nconst user: UserInterface = {\n    name:'小公',\n    gender:'男',\n    age:12,\n    run(n) {\n        console.log(`奔跑了${n}米`);\n    },\n}\nconsole.log(user);\nuser.run(2)\n\n```\n\n\n\n## \t5.9.3 定义函数结构\n\n```typescript\ninterface CountInterface {\n    (a: number, b: number): number\n}\nconst count : CountInterface = (x,y) =>{\n    return x + y\n}\nconsole.log(count(2,3));\n\n```\n\n\n\n## \t5.9.4 接口继承 \n\n​\textends 与 class 类继承类似\n\n```typescript\ninterface PersonInterface {\n    name: string\n    age: number\n}\ninterface StudentInterface extends PersonInterface{\n    garde: string\n}\nconst stu: StudentInterface = {\n    name:'张三',\n    age:22,\n    garde:'高三'\n}\n\n```\n\n\n\n## \t5.9.5 接口自动合并（可重复定义）\n\n```typescript\ninterface PersonInterface {\n    name: string\n    age: number\n}\ninterface PersonInterface {\n    speak():void\n}\nclass person implements PersonInterface {\n    name: string\n    age: number\n    constructor(name: string,age: number){\n        this.age = age\n        this.name = name\n    }\n    speak(): void {\n        console.log(`你好,我叫${this.name}`); \n    }\n}\n```\n\n\n\n# 6 - 相似概念区别\n\n## \t6.1 interface 与 type 的区别\n\n​\t\t**相同点**： interface与 type 都可以定义对象结构，在定义**对象结构**时，两者可以互换\n\n​\t\t**不同点**：1. interface ：更专注于定义**对象**和**类**的结构，支持**继承，合并**\n\n​\t\t\t\t\t\t2.type：可以定义**类型别名，联合类型，交叉类型**，但不支持继承和自动合并\n\n```typescript\n\u002F\u002F 都可以定义对象结构\ninterface PersonInterface {\n    name:string\n    age:number\n    speak():void\n}\ntype personType = {\n    name:string\n    age:number\n    speak():void\n}\n```\n\n## \t6.2 interface 与 抽象类的区别\n\n​\t\t相同点：都能定义一个**类的格式**\n\n​\t\t不同点：1.接口：**只能**描述**结构**，**不能**有任何的**代码实现**，一个类可以实现**多个**接口\n\n​\t\t\t\t\t\t2.抽象类：既可以包含**抽象方法**，也可以包含**具体方法**，一个类只能继承**一个**抽象类\n\n```typescript\n\u002F\u002F 一个类可以实现多个接口\n\ninterface FlyInterface{\n    fly(): void\n}\ninterface SwimInterface{\n    swim():void\n}\nclass Duck implements FlyInterface, SwimInterface{\n    fly(): void {\n        console.log('鸭子可以飞');\n    }\n    swim(): void {\n        console.log('也可以游泳');\n    }\n}\nconst duck = new Duck()\nduck.fly()\nduck.swim()\n    \n```\n\n",null,"1",5,0,1,"2026-04-07T16:11:17.502Z","2026-04-07T16:11:17.505Z","2026-05-26T19:42:46.098Z","0",{"id":28,"categoryName":37,"slug":38,"description":39,"sort":30,"isEnable":31,"createTime":40,"updateTime":41,"deleteTime":30},"前端开发","frontend-engineering","2222","2026-04-03T02:36:11.945Z","2026-04-07T16:38:46.496Z",[],[],[]]