TypeScript 继承与多态
本文献给:
已掌握 TypeScript 类基础(字段、构造函数、方法、访问修饰符、参数属性)的开发者。本文将带你学习类的继承(extends和super)、方法重写与override关键字,以及多态在 TypeScript 中的实现方式。
你将学到:
extends关键字实现类继承super调用父类构造函数与方法- 方法重写与
override关键字(TS 4.3+) - 多态的概念与示例
- 继承与访问修饰符的交互规则
目录
- 一、extends 继承
- 二、super 关键字
- 2.1 调用父类构造函数
- 2.2 调用父类方法
- 三、方法重写
- 3.1 override 关键字(TypeScript 4.3+)
- 四、多态
- 4.1 多态的优势
- 五、继承与访问修饰符的交互
- 5.1 public
- 5.2 protected
- 5.3 private
- 5.4 子类可以放宽访问权限吗?
- 六、常见错误与注意事项
- 6.1 忘记调用 super
- 6.2 在 super 之前访问 this
- 6.3 重写方法时返回类型不兼容
- 6.4 子类属性与父类属性同名
- 6.5 override 关键字版本要求
- 七、综合示例
- 八、小结
一、extends 继承
使用extends关键字让一个类继承另一个类的所有属性和方法。
classAnimal{name:string;constructor(name:string){this.name=name;}move(distance:number=0){console.log(`${this.name}moved${distance}m`);}}classDogextendsAnimal{bark(){console.log(`${this.name}barks`);}}constdog=newDog("Buddy");dog.bark();// "Buddy barks"dog.move(10);// "Buddy moved 10m"子类拥有父类的所有public和protected成员,private成员不可继承。
二、super 关键字
2.1 调用父类构造函数
如果在子类中定义了构造函数,则必须使用super()调用父类构造函数,且必须在访问this之前调用。
classAnimal{name:string;constructor(name:string){this.name=name;}}classDogextendsAnimal{breed:string;constructor(name:string,breed:string){super(name);// 必须先调用 superthis.breed=breed;}}2.2 调用父类方法
在子类方法中可以使用super.methodName()调用父类的原始方法。
classAnimal{speak(){console.log("Animal makes sound");}}classDogextendsAnimal{speak(){super.speak();// 调用父类方法console.log("Dog barks");}}constd=newDog();d.speak();// "Animal makes sound"// "Dog barks"三、方法重写
子类可以重写父类的方法,提供自己的实现。
classVehicle{start():void{console.log("Vehicle starting...");}}classCarextendsVehicle{start():void{console.log("Car engine roaring...");}}constmyCar=newCar();myCar.start();// "Car engine roaring..."3.1 override 关键字(TypeScript 4.3+)
TypeScript 4.3 引入了override关键字,用于显式标记方法重写父类方法。这可以避免因父类方法更名或删除而导致的意外错误。
classParent{greet(){console.log("Hello");}}classChildextendsParent{overridegreet(){console.log("Hi");}}如果父类中没有greet方法,override会报错:
classChildextendsParent{overridesayHi(){// ❌ 父类中没有 sayHi 方法console.log("Hi");}}override关键字让代码意图更清晰,推荐在重写方法时使用。
四、多态
多态是指同一个方法在不同子类中表现出不同的行为。父类类型的变量可以指向子类对象,调用重写的方法时,实际执行的是子类的实现。
classShape{area():number{return0;}}classCircleextendsShape{radius:number;constructor(radius:number){super();this.radius=radius;}overridearea():number{returnMath.PI*this.radius**2;}}classRectangleextendsShape{width:number;height:number;constructor(width:number,height:number){super();this.width=width;this.height=height;}overridearea():number{returnthis.width*this.height;}}functionprintArea(shape:Shape){console.log(`Area:${shape.area()}`);}constshapes:Shape[]=[newCircle(5),newRectangle(4,6)];shapes.forEach(printArea);// Area: 78.53981633974483// Area: 244.1 多态的优势
- 代码复用:使用父类类型的参数可以接受任意子类实例。
- 扩展性:新增子类无需修改已有函数。
五、继承与访问修饰符的交互
5.1 public
public成员在子类中保持public,可被任意访问。
5.2 protected
protected成员在子类内部可访问,但通过子类实例在外部不可访问。
classParent{protectedvalue=42;}classChildextendsParent{show(){console.log(this.value);// OK}}constc=newChild();console.log(c.value);// ❌ 属性受保护5.3 private
private成员不被继承,子类中无法访问。
classParent{privatesecret="hidden";}classChildextendsParent{try(){// console.log(this.secret); // ❌ 属性私有}}5.4 子类可以放宽访问权限吗?
子类重写方法时,不能降低父类方法的访问权限(即不能将public改为protected或private),但可以提升(将protected改为public是允许的)。
classParent{protecteddoSomething(){}}classChildextendsParent{publicdoSomething(){}// OK,放宽权限}六、常见错误与注意事项
6.1 忘记调用 super
在子类构造函数中,如果省略super(),TypeScript 会报错。
classChildextendsParent{constructor(){// 缺少 super() ❌}}6.2 在 super 之前访问 this
classChildextendsParent{constructor(){console.log(this);// ❌ 在 super 之前不能使用 thissuper();}}6.3 重写方法时返回类型不兼容
重写方法的返回类型必须是父类方法返回类型的子类型。
classParent{get():object{return{};}}classChildextendsParent{overrideget():string{// ❌ string 不是 object 的子类型return"hello";}}6.4 子类属性与父类属性同名
如果子类声明了与父类同名的属性,会覆盖父类的属性(但建议避免这样做,容易造成混淆)。
classParent{name="Parent";}classChildextendsParent{name="Child";// 覆盖}constc=newChild();console.log(c.name);// "Child"6.5 override 关键字版本要求
override需要 TypeScript 4.3+ 且noImplicitOverride标志启用(或严格模式)。如果没有启用,override只是普通标识符,但建议在tsconfig.json中开启"noImplicitOverride": true。
七、综合示例
// 抽象一个媒体播放器基类classMediaPlayer{protectedcurrentVolume:number=50;constructor(protectedname:string){}play():void{console.log(`${this.name}starts playing`);}pause():void{console.log(`${this.name}pauses`);}setVolume(volume:number):void{this.currentVolume=volume;console.log(`Volume set to${volume}`);}}// 音频播放器子类classAudioPlayerextendsMediaPlayer{privateequalizer:boolean=false;constructor(name:string,privatebitrate:number){super(name);}overrideplay():void{console.log(`AudioPlayer${this.name}plays at${this.bitrate}kbps`);}enableEqualizer():void{this.equalizer=true;console.log("Equalizer enabled");}// 重写并调用父类方法overridesetVolume(volume:number):void{super.setVolume(volume);console.log(`Audio volume adjusted to${volume}`);}}// 视频播放器子类classVideoPlayerextendsMediaPlayer{privatesubtitlesEnabled:boolean=false;overrideplay():void{console.log(`VideoPlayer${this.name}plays with subtitles:${this.subtitlesEnabled}`);}toggleSubtitles():void{this.subtitlesEnabled=!this.subtitlesEnabled;console.log(`Subtitles${this.subtitlesEnabled?"enabled":"disabled"}`);}// 不重写 setVolume,沿用父类行为}// 多态使用functioncontrolMedia(players:MediaPlayer[]){players.forEach(player=>{player.play();player.setVolume(70);player.pause();});}constaudio=newAudioPlayer("Spotify",320);constvideo=newVideoPlayer("Netflix");audio.enableEqualizer();video.toggleSubtitles();controlMedia([audio,video]);// 检查类型if(audioinstanceofAudioPlayer){audio.enableEqualizer();// 类型守卫后可用特有方法}八、小结
| 概念 | 语法/示例 | 说明 |
|---|---|---|
| 继承 | class Child extends Parent | 子类获得父类非私有成员 |
| super 调用构造函数 | super(args) | 子类构造函数中必须调用 |
| super 调用方法 | super.method() | 调用父类原始方法 |
| 方法重写 | 子类中定义同名方法 | 覆盖父类实现 |
| override 关键字 | override method() {} | 显式标记重写,增强安全性 |
| 多态 | 父类类型变量指向子类实例 | 调用重写方法执行子类版本 |
| 权限交互 | protected可被继承,private不可 | 子类可放宽但不能缩小访问权限 |
觉得文章有帮助?别忘了:
👍 点赞 👍– 给我一点鼓励
⭐ 收藏 ⭐– 方便以后查看
🔔 关注 🔔– 获取更新通知
标签:#TypeScript#继承#多态#面向对象#学习笔记#前端开发
