TypeScript 类与泛型的使用实践记录
TypeScript 作为 JavaScript 的超集,引入了静态类型检查、接口、类、枚举等特性,极大地提升了代码的可维护性和开发效率。其中,**类(Class)和泛型(Generics)**是 TypeScript 中两个非常重要的特性。本文将深入探讨 TypeScript 中泛型的使用方法和场景,以及如何通过类型约束来增强代码的灵活性和安全性。
一、TypeScript 类(Class)基础
在 TypeScript 中,类与 ES6 中的类类似,但 TypeScript 提供了更强大的类型检查和面向对象编程特性。
1.1 基本语法
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
const person = new Person('Alice', 30);
person.greet(); // 输出: Hello, my name is Alice
1.2 继承与多态
TypeScript 支持类的继承,通过 extends 关键字实现。
class Employee extends Person {
employeeId: number;
constructor(name: string, age: number, employeeId: number) {
super(name, age);
this.employeeId = employeeId;
}
greet() {
console.log(`Hello, my name is ${this.name} and my employee ID is ${this.employeeId}`);
}
}
const employee = new Employee('Bob', 25, 12345);
employee.greet(); // 输出: Hello, my name is Bob and my employee ID is 12345
二、TypeScript 泛型(Generics)基础
泛型允许我们在定义函数、接口或类时不预先指定具体的类型,而是在使用时再指定类型。这种方式提高了代码的复用性和灵活性。
2.1 基本语法
function identity<T>(arg: T): T {
return arg;
}
const num = identity<number>(42); // 类型为 number
const str = identity<string>('Hello'); // 类型为 string
2.2 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identityFn<T>(arg: T): T {
return arg;
}
const myIdentity: GenericIdentityFn<number> = identityFn;
2.3 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };
三、泛型的使用场景
3.1 通用数据结构
泛型常用于定义通用的数据结构,如链表、栈、队列等。
class Stack<T> {
private elements: T[] = [];
push(element: T) {
this.elements.push(element);
}
pop(): T | undefined {
return this.elements.pop();
}
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 输出: 2
const stringStack = new Stack<string>();
stringStack.push('a');
stringStack.push('b');
console.log(stringStack.pop()); // 输出: b
3.2 通用算法
泛型可以用于实现通用的算法,如排序、查找等。
function find<T>(array: T[], predicate: (item: T) => boolean): T | undefined {
for (const item of array) {
if (predicate(item)) {
return item;
}
}
return undefined;
}
const numbers = [1, 2, 3, 4, 5];
const foundNumber = find(numbers, n => n === 3);
console.log(foundNumber); // 输出: 3
const strings = ['a', 'b', 'c'];
const foundString = find(strings, s => s === 'b');
console.log(foundString); // 输出: b
四、类型约束(Type Constraints)
在泛型编程中,有时需要对类型参数进行约束,以确保类型参数具有某些属性或方法。TypeScript 提供了 extends 关键字来实现类型约束。
4.1 基本用法
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity({ length: 10, value: 'Hello' }); // 正确
loggingIdentity(3); // 错误: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'
4.2 使用 keyof 进行键约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const obj = { a: 1, b: 2, c: 3 };
console.log(getProperty(obj, 'a')); // 输出: 1
console.log(getProperty(obj, 'd')); // 错误: Argument of type '"d"' is not assignable to parameter of type '"a" | "b" | "c"'
4.3 组合类型约束
interface FirstLast {
first: string;
last: string;
}
function createFullName<T extends FirstLast>(obj: T): string {
return `${obj.first} ${obj.last}`;
}
const person = { first: 'John', last: 'Doe', age: 30 };
console.log(createFullName(person)); // 输出: John Doe
const invalidPerson = { first: 'Jane' };
console.log(createFullName(invalidPerson)); // 错误: Property 'last' is missing in type '{ first: string; }' but required in type 'FirstLast'
五、泛型的优势
- 类型安全: 泛型在编译时进行类型检查,避免了运行时错误。
- 代码复用: 泛型允许编写更通用的代码,减少重复代码。
- 灵活性: 泛型提供了高度的灵活性,可以在不牺牲类型安全的前提下实现多种类型操作。
- 可维护性: 通过泛型,代码更具可读性和可维护性。
六、常见误区与注意事项
- 过度使用泛型: 泛型虽然强大,但过度使用可能导致代码复杂化。应根据实际需求合理使用。
- 类型约束不足: 在使用泛型时,确保