您好,登录后才能下订单哦!
TypeScript是一种由微软开发的开源编程语言,它是JavaScript的一个超集,添加了可选的静态类型和基于类的面向对象编程。TypeScript的设计目标是开发大型应用,它可以编译成纯JavaScript,运行在任何浏览器、任何计算机和任何操作系统上。
在TypeScript中,extends
关键字是一个非常重要的概念,它在多种场景下都有广泛的应用。本文将详细介绍extends
关键字在TypeScript中的使用方式,包括类继承、接口扩展、泛型约束等。
在面向对象编程中,继承是一个核心概念。TypeScript通过extends
关键字支持类的继承。通过继承,子类可以继承父类的属性和方法,并且可以添加新的属性和方法,或者重写父类的方法。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog('Rex');
dog.move(10); // Rex moved 10m.
dog.bark(); // Woof! Woof!
在这个例子中,Dog
类继承了Animal
类。Dog
类不仅可以使用Animal
类的move
方法,还可以定义自己的bark
方法。
子类可以重写父类的方法,以满足特定的需求。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Snake extends Animal {
move(distance: number = 5) {
console.log('Slithering...');
super.move(distance);
}
}
const snake = new Snake('Sammy');
snake.move(); // Slithering... Sammy moved 5m.
在这个例子中,Snake
类重写了Animal
类的move
方法,并在其中调用了super.move(distance)
,以保留父类的行为。
super
在子类的构造函数中,必须调用super()
来初始化父类的属性。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Horse extends Animal {
constructor(name: string) {
super(name); // 必须调用super()
}
move(distance: number = 45) {
console.log('Galloping...');
super.move(distance);
}
}
const horse = new Horse('Tommy');
horse.move(34); // Galloping... Tommy moved 34m.
在这个例子中,Horse
类的构造函数中调用了super(name)
,以确保Animal
类的name
属性被正确初始化。
在TypeScript中,接口(Interface)用于定义对象的形状。通过extends
关键字,一个接口可以扩展另一个接口,从而继承其属性和方法。
interface Person {
name: string;
age: number;
}
interface Employee extends Person {
employeeId: number;
}
const employee: Employee = {
name: 'John',
age: 30,
employeeId: 12345
};
在这个例子中,Employee
接口扩展了Person
接口,因此Employee
接口包含了Person
接口的所有属性,并且添加了employeeId
属性。
一个接口可以同时扩展多个接口。
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: number;
}
interface Manager extends Person, Employee {
department: string;
}
const manager: Manager = {
name: 'Alice',
age: 35,
employeeId: 67890,
department: 'HR'
};
在这个例子中,Manager
接口同时扩展了Person
和Employee
接口,因此Manager
接口包含了Person
和Employee
接口的所有属性,并且添加了department
属性。
在TypeScript中,接口不仅可以扩展其他接口,还可以扩展类。当一个接口扩展一个类时,它会继承类的成员,但不包括其实现。
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
interface Employee extends Person {
employeeId: number;
}
const employee: Employee = {
name: 'John',
employeeId: 12345,
greet() {
console.log(`Hello, my name is ${this.name} and my employee ID is ${this.employeeId}`);
}
};
employee.greet(); // Hello, my name is John and my employee ID is 12345
在这个例子中,Employee
接口扩展了Person
类,因此Employee
接口包含了Person
类的name
属性和greet
方法。需要注意的是,接口扩展类时,不会继承类的实现,因此Employee
接口中的greet
方法需要重新定义。
在TypeScript中,泛型(Generics)允许我们创建可重用的组件,这些组件可以处理多种类型的数据。通过extends
关键字,我们可以对泛型进行约束,以确保泛型参数符合特定的条件。
function identity<T>(arg: T): T {
return arg;
}
const output = identity<string>('Hello');
console.log(output); // Hello
在这个例子中,identity
函数是一个泛型函数,它可以接受任何类型的参数,并返回相同类型的值。通过<T>
语法,我们定义了一个泛型参数T
。
有时候,我们希望泛型参数只能是某些特定的类型。这时,我们可以使用extends
关键字对泛型进行约束。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在我们知道arg有length属性
return arg;
}
loggingIdentity('Hello'); // 5
loggingIdentity([1, 2, 3]); // 3
loggingIdentity({ length: 10, value: 'Hello' }); // 10
在这个例子中,loggingIdentity
函数的泛型参数T
被约束为必须实现Lengthwise
接口的类型。因此,arg
参数必须具有length
属性。
一个泛型参数可以同时满足多个约束条件。
interface Lengthwise {
length: number;
}
interface Printable {
print(): void;
}
function loggingIdentity<T extends Lengthwise & Printable>(arg: T): T {
console.log(arg.length); // 现在我们知道arg有length属性
arg.print(); // 现在我们知道arg有print方法
return arg;
}
const obj = {
length: 10,
print() {
console.log('Printing...');
}
};
loggingIdentity(obj); // 10, Printing...
在这个例子中,loggingIdentity
函数的泛型参数T
被约束为必须同时实现Lengthwise
和Printable
接口的类型。因此,arg
参数必须具有length
属性和print
方法。
泛型约束也可以应用于类。
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
class Employee extends Person {
employeeId: number;
constructor(name: string, employeeId: number) {
super(name);
this.employeeId = employeeId;
}
work() {
console.log(`${this.name} is working.`);
}
}
function printEmployee<T extends Person>(employee: T): void {
employee.greet();
if (employee instanceof Employee) {
employee.work();
}
}
const employee = new Employee('John', 12345);
printEmployee(employee); // Hello, my name is John, John is working.
在这个例子中,printEmployee
函数的泛型参数T
被约束为必须继承Person
类的类型。因此,employee
参数必须具有greet
方法。如果employee
是Employee
类的实例,还可以调用work
方法。
在TypeScript中,条件类型(Conditional Types)允许我们根据类型之间的关系来选择不同的类型。条件类型通常与extends
关键字一起使用。
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
在这个例子中,IsString
是一个条件类型,它根据T
是否扩展string
类型来选择true
或false
。
当条件类型应用于联合类型时,它会自动分发到联合类型的每个成员上。
type ToArray<T> = T extends any ? T[] : never;
type A = ToArray<string | number>; // string[] | number[]
在这个例子中,ToArray
条件类型被应用于string | number
联合类型,因此结果是string[] | number[]
。
条件类型可以与泛型约束结合使用,以实现更复杂的类型逻辑。
type NonNullable<T> = T extends null | undefined ? never : T;
type A = NonNullable<string | number | null | undefined>; // string | number
在这个例子中,NonNullable
条件类型用于过滤掉null
和undefined
类型。
条件类型还可以与映射类型结合使用,以实现更灵活的类型转换。
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
class Person {
name: string;
age: number;
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
type PersonFunctionProperties = FunctionProperties<Person>; // { greet: () => void }
在这个例子中,FunctionPropertyNames
条件类型用于提取Person
类中所有方法的名称,然后FunctionProperties
映射类型用于提取这些方法。
在TypeScript中,extends
关键字还可以用于定义高级类型,如交叉类型、联合类型、映射类型等。
交叉类型(Intersection Types)是将多个类型合并为一个类型。
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: number;
}
type EmployeePerson = Person & Employee;
const employeePerson: EmployeePerson = {
name: 'John',
age: 30,
employeeId: 12345
};
在这个例子中,EmployeePerson
类型是Person
和Employee
类型的交叉类型,因此它包含了Person
和Employee
接口的所有属性。
联合类型(Union Types)表示一个值可以是多种类型之一。
type StringOrNumber = string | number;
function printValue(value: StringOrNumber) {
console.log(value);
}
printValue('Hello'); // Hello
printValue(123); // 123
在这个例子中,StringOrNumber
类型表示一个值可以是string
或number
类型。
映射类型(Mapped Types)允许我们基于现有类型创建新类型。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Person = {
name: string;
age: number;
};
type ReadonlyPerson = Readonly<Person>;
const person: ReadonlyPerson = {
name: 'John',
age: 30
};
// person.name = 'Alice'; // Error: Cannot assign to 'name' because it is a read-only property.
在这个例子中,Readonly
映射类型用于将Person
类型的所有属性设置为只读。
条件映射类型结合了条件类型和映射类型,允许我们根据条件选择不同的映射方式。
type NonNullablePropertyKeys<T> = {
[K in keyof T]: T[K] extends null | undefined ? never : K;
}[keyof T];
type NonNullableProperties<T> = Pick<T, NonNullablePropertyKeys<T>>;
type Person = {
name: string;
age: number | null;
address: string | undefined;
};
type NonNullablePerson = NonNullableProperties<Person>; // { name: string }
在这个例子中,NonNullablePropertyKeys
条件映射类型用于提取Person
类型中所有非null
和非undefined
属性的名称,然后NonNullableProperties
映射类型用于提取这些属性。
在TypeScript中,extends
关键字是一个非常重要的概念,它在类继承、接口扩展、泛型约束、条件类型和高级类型中都有广泛的应用。通过extends
关键字,我们可以实现代码的复用、类型的安全性和灵活性。
extends
关键字,子类可以继承父类的属性和方法,并且可以添加新的属性和方法,或者重写父类的方法。extends
关键字,一个接口可以扩展另一个接口,从而继承其属性和方法。extends
关键字,我们可以对泛型进行约束,以确保泛型参数符合特定的条件。extends
关键字,我们可以根据类型之间的关系来选择不同的类型。extends
关键字,我们可以定义交叉类型、联合类型、映射类型等高级类型。掌握extends
关键字的使用,可以帮助我们编写更加灵活、安全和可维护的TypeScript代码。希望本文对你理解和使用extends
关键字有所帮助。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。