typescript extends 关键字的基本用法
1. 泛型约束
在泛型中,extends
用于约束类型参数必须是某个类型或其子类型。这是 TypeScript 中一种使用 extends
的常见方式。
例子:泛型中的
extends
scala
type MyType<T extends number> = { value: T };
let numType: MyType<number> = { value: 42 }; // 正确
// let strType: MyType<string> = { value: "hello" }; // 错误,因为 string 不符合 extends number 约束
例子:条件类型中的
extends
typescript
type MyConditionalType<T> = T extends string ? number : boolean;
let result1: MyConditionalType<string> = 42; // number
let result2: MyConditionalType<boolean> = true; // boolean
2. 接口继承
在接口中,extends
用于声明一个接口继承另一个接口。这样,新的接口将包含父接口的成员,并可以添加额外的成员。
typescript
interface Animal {
name: string;
makeSound(): void;
}
interface Dog extends Animal {
bark(): void;
}
const myDog: Dog = {
name: 'Buddy',
makeSound() {
console.log(this.name + ' makes a sound');
},
bark() {
console.log(this.name + ' barks');
}
};
在这个例子中,Dog
接口通过 extends
继承了 Animal
接口的属性和方法。
需要注意的是,在 TypeScript 中的 extends
主要用于类型系统中的约束和声明继承关系,并不会在运行时创建真正的类继承关系。 TypeScript 的继承更多地关注于类型的静态检查,而不是在运行时创建对象之间的实际继承链。
extends
用于约束类型参数必须是某个类型或其子类型
这句话是什么意思呢?我来解释一下:
当在 TypeScript 中使用 extends
关键字作为类型参数的约束时,意味着该类型必须是指定类型或该类型的子类型。让我们通过一个例子来说明:
typescript
// 定义一个基础类型
interface Animal {
name: string;
}
// 通过扩展基础类型创建一个子类型
interface Dog extends Animal {
breed: string;
}
// 泛型类型,约束类型参数必须是 Animal 或其子类型
type MyType<T extends Animal> = {
animalData: T;
sound(): void;
};
// 使用一个子类型
const dogData: Dog = { name: 'Buddy', breed: 'Labrador' };
// 使用具有子类型的泛型类型
const myDogType: MyType<Dog> = {
animalData: dogData,
sound() {
console.log(`${dogData.name} 会叫`);
}
};
myDogType.sound(); // 输出 "Buddy 会叫"
在这个例子中:
Animal
是一个基础类型。Dog
是Animal
的子类型,因为它扩展了Animal
。MyType
是一个泛型类型,它接受类型参数T
,该参数必须是Animal
或其子类型。
当我们使用 MyType<Dog>
时,我们在说 T
是 Dog
或 Dog
的子类型。这是有效的,因为 Dog
是 Animal
的子类型,满足了约束条件。
因此,在这个例子中,子类型是指任何扩展或继承指定基础类型(在这里是 Animal
)的类型。
typescript 中的 extends 和 js 中class 的extends 有什么区别?
虽然 TypeScript 中的 extends
关键字与 JavaScript 中的 class
的 extends
关键字在表面上看起来相似,但它们实际上有不同的用途和含义。
TypeScript 中的 extends
在 TypeScript 中,extends
用于类型系统中的泛型和条件类型。在泛型中,extends
用于约束一个类型参数必须是某个类型或其子类型。在条件类型中,extends
用于进行条件判断,根据某个条件选择不同的类型。
例子:泛型中的 extends
scala
type MyType<T extends number> = { value: T };
let numType: MyType<number> = { value: 42 }; // 正确
// let strType: MyType<string> = { value: "hello" }; // 错误,因为 string 不符合 extends number 约束
例子:条件类型中的 extends
typescript
type MyConditionalType<T> = T extends string ? number : boolean;
let result1: MyConditionalType<string> = 42; // number
let result2: MyConditionalType<boolean> = true; // boolean
JavaScript 中的 extends
在 JavaScript 中的 class
关键字的 extends
用于实现类的继承。子类继承父类的属性和方法,并且可以添加自己的属性和方法。
例子:JavaScript 中的类继承
javascript
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a sound');
}
}
class Dog extends Animal {
speak() {
console.log(this.name + ' barks');
}
}
const myDog = new Dog('Buddy');
myDog.speak(); // 输出 "Buddy barks"
在这个例子中,Dog
类继承自 Animal
类,使用了 extends
关键字。
总结:
- TypeScript 中的
extends
主要用于泛型约束和条件类型,是用于类型系统的。 - JavaScript 中的
extends
用于类继承,是用于实现面向对象的类继承的。
虽然与 JavaScript 中的 class
的 extends
有些相似之处,但在 TypeScript 中的 extends
不涉及实际的运行时继承。
什么是运行时继承呢?
运行时继承指的是在程序实际运行时,一个对象能够继承另一个对象的属性和方法。这是面向对象编程中的一个概念,其中一个类(或构造函数)的实例能够继承另一个类的实例的特性。在传统的面向对象语言中,如Java、C++、Python等,继承是在运行时创建对象之间的实际关联和共享属性与方法。
具体来说,当一个类继承另一个类时,它获得了父类的属性和方法,并且可以通过运行时创建的对象来访问和调用这些继承的成员。这样的继承关系在程序执行时动态地影响着对象之间的关系。
在JavaScript中,class
的 extends
关键字可以用于实现运行时继承。以下是一个简单的例子:
javascript
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a sound');
}
}
class Dog extends Animal {
bark() {
console.log(this.name + ' barks');
}
}
const myDog = new Dog('Buddy');
myDog.speak(); // 输出 "Buddy makes a sound"
myDog.bark(); // 输出 "Buddy barks"
在这个例子中,Dog
类继承了 Animal
类,创建 myDog
对象后,myDog
实例既有 Dog
类的 bark
方法,也有 Animal
类的 speak
方法。这是典型的运行时继承。
相比之下,TypeScript 中的 extends
关键字主要用于静态类型检查,不会在实际运行时创建对象之间的真正继承链。TypeScript 中的继承更关注于在编译阶段进行类型检查,以确保代码的类型安全性。所以,虽然 TypeScript 提供了 extends
关键字,但它不会在运行时创建类继承关系,而是在编译时进行类型检查。