在 TypeScript 的第一章中,我们了解了类型注解、类和接口、函数、基本类型和高级类型等基础知识。在本章中,我们将深入学习类型注解的进阶用法、类的继承和多态、泛型、模块和命名空间等高级特性。
进阶的类型注解
在 TypeScript 的第一章中,我们介绍了类型注解的基本用法。在本节中,我们将介绍类型注解的进阶用法,包括类型别名、交叉类型、联合类型和类型保护等。
类型别名
类型别名可以帮助我们为复杂的类型定义更容易记忆和使用的名称。以下是一个类型别名的示例:
type Person = {
name: string;
age: number;
};
function greet(person: Person) {
console.log(`Hello, ${person.name}!`);
}
在这个示例中,我们使用 type 关键字定义了一个名为 Person 的类型别名,它代表了一个拥有 name 和 age 属性的对象类型。在 greet 函数中,我们使用 Person 类型别名来指定参数 person 的类型。
交叉类型
交叉类型可以帮助我们将多个类型合并为一个类型。以下是一个交叉类型的示例:
type Cat = {
name: string;
meow(): void;
};
type Fish = {
swim(): void;
};
type CatFish = Cat & Fish;
let catFish: CatFish = {
name: "Tom",
meow() {
console.log(`${this.name} says meow`);
},
swim() {
console.log(`${this.name} is swimming`);
},
};
在这个示例中,我们使用 & 操作符将 Cat 和 Fish 类型合并为一个类型别名 CatFish。然后,我们创建了一个 CatFish 类型的对象 catFish,它包含了 Cat 和 Fish 类型的属性和方法。
联合类型
联合类型可以帮助我们指定一个变量的多种可能类型。以下是一个联合类型的示例:
let value: string | number;
value = "hello";
console.log(value.length);
value = 10;
console.log(value.toFixed(2));
在这个示例中,我们使用 | 操作符指定变量 value 的类型为字符串或数字。当我们将变量赋值为字符串时,我们可以使用字符串的 length 属性;当我们将变量赋值为数字时,我们可以使用数字的 toFixed 方法。
类型保护
当我们使用联合类型时,我们需要使用类型保护来确定变量的具体类型。以下是一个类型保护的示例:
function printValue(value: string | number) {
if (typeof value === "string") {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
printValue("hello");
printValue(10.123);
在这个示例中,我们使用 typeof 操作符来判断变量 value 的类型。当变量的类型为字符串时,我们可以使用字符串的 toUpperCase 方法;当变量的类型为数字时,我们可以使用数字的 toFixed 方法。
类的继承和多态
类的继承和多态是面向对象编程的核心概念之一,它们可以帮助我们组织代码和实现代码的复用。在 TypeScript 中,类的继承和多态同样可以实现。
类的继承
类的继承可以帮助我们从已有的类中派生出新的类,并扩展它们的功能。以下是一个类的继承的示例:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log(`Hello, my name is ${this.name}.`);
}
}
class Cat extends Animal {
meow()() {
console.log(`${this.name} says meow`);
}
}
let cat = new Cat("Tom");
cat.sayHello();
cat.meow();
在这个示例中,我们定义了一个 Animal 类,它包含了一个名为 name 的属性和一个名为 sayHello 的方法。然后,我们定义了一个 Cat 类,它从 Animal 类中继承了 name 属性和 sayHello 方法,并扩展了一个名为 meow 的方法。最后,我们创建了一个 Cat 类的实例 cat,并调用了它的 sayHello 和 meow 方法。
多态
多态可以帮助我们使用基类的引用来调用派生类的方法。以下是一个多态的示例:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log(`Hello, my name is ${this.name}.`);
}
}
class Cat extends Animal {
meow() {
console.log(`${this.name} says meow`);
}
}
class Dog extends Animal {
bark() {
console.log(`${this.name} says woof`);
}
}
let animals: Animal[] = [new Cat("Tom"), new Dog("Max")];
for (let animal of animals) {
animal.sayHello();
if (animal instanceof Cat) {
animal.meow();
} else if (animal instanceof Dog) {
animal.bark();
}
}
在这个示例中,我们定义了一个 Animal 类和两个派生类 Cat 和 Dog。然后,我们创建了一个 Animal 类型的数组 animals,其中包含了一个 Cat 类型的实例和一个 Dog 类型的实例。最后,我们循环遍历 animals 数组,并使用基类的引用来调用派生类的方法。
泛型
泛型可以帮助我们编写更加通用和灵活的代码。在 TypeScript 中,泛型可以应用于函数、类和接口等场景。
泛型函数
泛型函数可以帮助我们编写可以适用于多种类型的函数。以下是一个泛型函数的示例:
function reverse<T>(items: T[]): T[] {
return items.reverse();
}
let numbers = [1, 2, 3, 4, 5];
let reversedNumbers = reverse(numbers);
console.log(reversedNumbers);
let strings = ["hello", "world"];
let reversedStrings = reverse(strings);
console.log(reversedStrings);
在这个示例中,我们定义了一个名为 reverse 的泛型函数,它接受一个类型为 T 的数组作为参数,并返回一个类型为 T 的数组。然后,我们分别调用了 reverse 函数,并传递了一个数字类型的数组和一个字符串类型的数组作为参数。
泛型类
泛型类可以帮助我们编写可以适用于多种类型的类。以下是一个泛型类的示例:
class Stack<T> {
private items: T[] = [];
push(item: T) {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
let stack = new Stack<number>();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.pop());
console.log(stack.pop());
console.log(stack.pop());
let stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
console.log(stringStack.pop());
console.log(stringStack.pop());
在这个示例中,我们定义了一个名为 Stack 的泛型类,它包含了一个名为 items 的私有属性和两个公共方法 push 和 pop。然后,我们分别创建了一个 Stack 类型的实例和一个 Stack 类型的实例,并分别调用了它们的 push 和 pop 方法。
泛型接口
泛型接口可以帮助我们定义可以适用于多种类型的接口。以下是一个泛型接口的示例:
interface Pair<T, U> {
first: T;
}
在这个示例中,我们定义了一个泛型接口 Pair,它有两个类型参数 T 和 U。这个接口有一个属性 first,它的类型是 T。
以上是 TypeScript 的类型系统的一些基础知识。在接下来的学习中,我们将深入了解这些概念,并学习更多高级特性。