keyipatience:个人主页作者简介C/C后端开发学习者专栏传送门《c》《linux》《c高阶数据结构》《c数据结构与算法》《前端学习》⭐️patience is key in life枚举 enum枚举enum定义一组命名常量限定一个值只能是里面其中之一作用等价于字面量类型 联合类型。通过 .方式访问// 定义枚举 enum Direction { Up, Down, Left, Right } // 枚举直接当作类型注解 function changeDirection(direction: Direction) { console.log(direction) }默认数字枚举enum Direction { Up, // 0 Down, // 1 Left, // 2 Right // 3 } changeDirection(Direction.Up) // 传枚举成员也可以手动设置起始值enum Direction { Up 10, Down, // 11 Left, // 12 Right // 13 }和「字面量联合类型」对比// 字面量联合写法 type Direction Up | Down | Left | Right字符串枚举常用enum Direction { Up UP, Down DOWN, Left LEFT, Right RIGHT } changeDirection(Direction.Up)枚举的特点和原理enum 是 TS 里少数编译后还保留 JS 代码的特性普通type、接口这些只是类型编译成 JS 直接删掉 枚举不只是类型它编译后会生成真实 JS 对象运行时也存在。// TS源码字符串枚举 enum Direction { Up UP, Down DOWN, Left LEFT, Right RIGHT }编译出来的 JSvar Direction; (function (Direction) { Direction[Up] UP; Direction[Down] DOWN; Direction[Left] LEFT; Direction[Right] RIGHT; })(Direction || (Direction {}));对比枚举 vs 字面量联合类型// 字面量联合编译后完全消失运行时不存在 type Direction UP | DOWN | LEFT | RIGHTenum编译后有 JS 代码运行时可用可以动态遍历、取值但是代码体积更大type 联合编译后直接抹除无运行时代码更轻量简洁日常开发优先推荐字面量联合类型any 类型any任意类型。标记为any的变量可以做任意操作TS 不再做类型检查相当于关闭类型保护let obj: any { x: 0 } obj.bar 100 // ✅ 允许随便新增属性 obj() // ✅ 允许把对象当成函数调用运行时会报错但TS不提示 const n: number obj // ✅ 允许直接赋值给number类型TS不拦截重点原则尽量不要用 anyTS 的核心价值就是类型校验any 会直接废掉这个能力。 只有一种场景临时用类型极其复杂、临时跳过类型校验优先想别的方案代替。隐式 any自动变成 any 的两种情况1.只声明变量不写类型注解、也不给初始值let a; // 隐式 any ❌ 不推荐2.函数参数不写类型注解function fn(p) { // p 隐式 any ❌ }规范这两种情况都要手动补上类型。和 unknown 的简单区分any随便读、随便调用、随便赋值给别的类型无限制unknown也是未知类型但不能随便操作必须先做类型判断更安全是 any 的优选替代方案typeof类型查询区分两个完全不一样的typeof1.JS 运行时 typeof在代码执行时得到字符串string/numberconsole.log(typeof Hello world) // 运行输出string const num 99 type NumType typeof num // NumType 就是 number const str abc type StrType typeof str // string2.TS 类型上下文的 typeof编译期拿到变量 / 属性的类型用来写类型注解示例对比let p { x: 1, y: 2 } // 写法1手动写出对象类型即point的类型是XXX function formatPoint(point: { x: number; y: number }) {} // 写法2TS typeof直接拿到 p 的类型简化代码 ✅ function formatPoint(point: typeof p) {}typeof p等价于{ x: number; y: number }class的基本使用TS 兼容 ES6 的class还额外增加类型注解、访问修饰符public/private/protected等能力。class Person {} // 写法1自动类型推断p 的类型是 Person推荐 const p new Person() // 写法2手动写类型注解 Person const p: Person new Person()class本身可以直接当作类型使用在 TS 里类有双重身份运行时是构造函数编译后 JS 真实存在可以new类型层面代表这个类实例的类型所以写p:Person写new Person()的时候TS 会自动推断出变量类型是Person所以类型注解: Person可以省略。实例属性初始化class Person { // 1. 只写类型注解没有初始值 age: number // 2. 直接赋值初始值TS自动推断类型可以省略类型注解 gender 男 // 等价于gender: string 男 }构造函数 constructorclass Person { age: number gender: string constructor(age: number, gender: string) { this.age age this.gender gender } }1.必须先在类里声明实例成员age: number、gender: string声明之后才能在构造函数里用this.age/this.gender赋值、访问。JS 可以直接this.xxx不用提前声明TS 要求要先声明。2.constructor 的特点考点参数必须加类型注解不然参数隐式变成any构造函数不要写返回值类型它自动返回当前类的实例不能写: Person。实例实现方法class Point { x 10 y 10 scale(n: number): void { this.x * n this.y * n } } const p new Point() p.scale(2)类继承extends和implementsextends继承另一个类拿到父类的属性、方法JS、TS 都能用implements实现接口只遵守接口的类型约定不会继承任何代码TS 独有JS 没有extends继承类:class Animal { move() { console.log(Moving along!) } } // Dog 继承 Animal class Dog extends Animal { bark() { console.log(汪) } } const dog new Dog() dog.move() // 来自父类Animal dog.bark() // 自己的方法implements 实现接口interface Singable { sing(): void } class Person implements Singable { sing() { console.log(你是我的小呀小苹果儿) } } //一个类可以同时实现多个接口 interface Singable { sing():void } interface Runable { run():void } class Person implements Singable, Runable { sing(){} run(){} } 也可以一边继承父类、一边实现接口 class Student extends Person implements Singable {}interface接口只定义规范定义一个标准需要有哪些方法、属性没有实现代码。implements写在类上表示这个类遵守这个接口的约定遵守该接口规则。强制要求类里面必须实现接口声明的全部成员少一个 TS 就报错。访问修饰符 public /private/protected一段代码直接看出区别class Person { // public公开任何地方都能访问默认修饰符可以不写 public name: string // private私有**只能在当前类内部**访问子类、实例都访问不到 private age: number // protected受保护当前类 子类内部可以访问外部实例不能访问 protected gender: string constructor(name: string, age: number, gender: string) { this.name name this.age age this.gender gender } say() { console.log(this.name, this.age, this.gender) // 类内部三个全都能访问 } } class Student extends Person { study() { console.log(this.name) // ✅ public 可以 console.log(this.gender) // ✅ protected 子类内部可以 // console.log(this.age) // ❌ private子类拿不到 } } const p new Person(张三, 18, 男) console.log(p.name) // ✅ public外部可以 // console.log(p.age) // ❌ private外部报错 // console.log(p.gender) // ❌ protected外部实例访问报错构造函数参数上可以直接加修饰符语法糖class Person { // 直接自动生成属性省去单独声明 constructor(public name:string, private age:number) {} }展开后的完整等价代码class Person { public name: string private age: number constructor(name: string, age: number) { this.name name this.age age } }readonly 只读修饰符作用属性只能初始化一次之后不能修改只能读。class Person { readonly id: number//也可以直接在这里赋值 name: string constructor(id: number, name: string) { this.id id // ✅ 构造函数里可以赋值唯一可以修改的地方 this.name name } } const p new Person(1001, 小明) console.log(p.id) // ✅ 可以读取 // p.id 2002 // ❌ 报错readonly实例创建后禁止修改 p.name 小红 // ✅ 普通属性正常修改还可以搭配语法糖构造参数简写class Person { constructor(readonly id: number, public name: string) {} }readonly只限制重新赋值如果属性是对象 / 数组里面的内容依然可以改class Test { readonly arr: number[] [1,2,3] } const t new Test() t.arr.push(4) // ✅ 合法只是不能 t.arr [9,9,9]类型兼容性1.结构化类型系统StructuralTS、Python看形状有哪些属性、方法不看名字。长得一样就兼容。 俗称鸭子类型看起来像鸭子、叫起来像鸭子 → 就是鸭子。class Point { x: number; y: number } class Point2D { x: number; y: number } const p: Point new Point2D() // ✅ 不报错虽然是两个完全独立的类名字不一样但成员一模一样结构匹配赋值合法。2.标明类型系统NominalJava / C# / C看类型名字本身必须是同一个类或者有继承关系才可以赋值。 上面这段代码放到 Java 直接编译报错Point和Point2D就是两个无关类型。对象的类容兼容性多的可以赋值给少的反过来不行class Point { x: number; y: number } // 少x、y class Point3D { x: number; y: number; z: number } // 多x、y、z const p: Point new Point3D() // ✅ 合法 // const p3: Point3D new Point() // ❌ 报错Point 缺 z接口的类型兼容性规则和 class 对象的兼容规则一模一样看结构不看名字多的可以赋值给少的反过来不行interface Point { x: number; y: number } interface Point2D { x: number; y: number } let p1: Point let p2: Point2D p1 // ✅ 结构完全一样可以赋值 interface Point3D { x: number; y: number; z: number } let p3: Point3D p2 p3 // ✅ Point3D 有 x,y满足 Point2D 的要求多了z没关系 // p3 p2 // ❌ 报错p2 缺少 zinterface 和 class 互相之间也可以兼容class Point3D { x: number; y: number; z: number } let p3: Point2D new Point3D() // ✅ 合法函数兼容性参数个数角度和前面反过来少的可以给多的多的不能给少的type F1 (a: number) void type F2 (a: number, b: number) void let f1: F1 let f2: F2 f1 // ✅ 合法参数少的 f1 赋值给参数多的 f2参数类型角度相同位置的参数原始类型要一模一样type F1 (a: number) string type F2 (a: number) string let f1: F1 let f2: F2 f1 // ✅ 可以对应位置参数类型相同 type F3 (a: string) string // f2 f3 ❌a一个是number、一个是string原始类型不匹配如果参数是对象走对象兼容多的给少的interface P {x:number} interface P3 {x:number; y:number} type FA (p: P)void//参数是pp的类型是P即对象 type FB (p: P3)void let fa: FA let fb: FB fa fb // ✅ P3包含P的属性对象兼容返回值类型角度原始类型就简单返回值类型必须相同type Fa () string type Fb () string let fa: Fa let fb: Fb fa // ✅ type Fc () number // fa Fc ❌ string 和 number 不兼容同样如果参数是对象多的给少的type F1 () {x: number}//返回一个对象对象必须有 x数字 type F2 () {x: number; y: number}//返回对象同时有 x 和 y let f1: F1 let f2: F2 f1 f2 // ✅ OK // f2 f1 // ❌ 报错f1返回值少y
阅读完成 · 觉得有帮助?