TypeScript -泛型类
你好,未来的编程巨星们!今天,我们将深入TypeScript泛型类的精彩世界。如果你是编程新手,不用担心;我会像过去几年指导无数学生一样,一步步引导你。所以,拿起你最喜欢的饮料,舒服地坐下来,让我们一起踏上这段冒险之旅吧!
泛型类
什么是泛型类?
想象你在一个冰淇淋店,但不是选择口味,而是挑选数据类型。这就是泛型类的本质!它们允许我们创建灵活、可重用的组件,可以处理不同的数据类型,同时不牺牲类型安全。
让我们从一个简单的例子开始:
class Box<T> {
private content: T;
constructor(value: T) {
this.content = value;
}
getValue(): T {
return this.content;
}
}
在这个例子中,Box
是一个泛型类。<T>
就像是一个我们稍后指定的类型的占位符。就像告诉冰淇淋店:“我下单时会决定口味!”
让我们分解一下:
-
class Box<T>
:这声明了一个名为Box
的泛型类,具有类型参数T
。 -
private content: T
:我们在说content
将是类型T
,无论T
是什么。 -
constructor(value: T)
:构造函数接受一个类型为T
的值。 -
getValue(): T
:此方法返回类型为T
的值。
现在,让我们看看如何使用这个类:
let numberBox = new Box<number>(42);
console.log(numberBox.getValue()); // 输出: 42
let stringBox = new Box<string>("你好,TypeScript!");
console.log(stringBox.getValue()); // 输出: 你好,TypeScript!
这酷不酷?我们使用了同一个Box
类来存储数字和字符串。就像有一个魔法盒子,可以容纳你放入的任何东西,但仍然记得它持有的确切类型!
多个类型参数
有时,一个类型参数不够。让我们创建一个更复杂的例子,包含多个类型参数:
class Pair<T, U> {
private first: T;
private second: U;
constructor(first: T, second: U) {
this.first = first;
this.second = second;
}
getFirst(): T {
return this.first;
}
getSecond(): U {
return this.second;
}
}
这个Pair
类可以持有两个可能不同类型的值。就像有一个双冰淇淋蛋筒,每个球可以是不同的口味!
让我们使用我们的Pair
类:
let pair = new Pair<string, number>("年龄", 30);
console.log(pair.getFirst()); // 输出: 年龄
console.log(pair.getSecond()); // 输出: 30
泛型约束
有时,我们希望限制可以在我们的泛型类中使用哪些类型。我们可以通过使用约束来实现。就像说:“你可以选择任何冰淇淋口味,只要它不是太辣的!”
interface Lengthwise {
length: number;
}
class LengthChecker<T extends Lengthwise> {
checkLength(obj: T): string {
return `长度是: ${obj.length}`;
}
}
在这个例子中,T extends Lengthwise
意味着T
必须是具有length
属性的类型。让我们使用它:
let stringChecker = new LengthChecker<string>();
console.log(stringChecker.checkLength("你好")); // 输出: 长度是: 3
let arrayChecker = new LengthChecker<number[]>();
console.log(arrayChecker.checkLength([1, 2, 3])); // 输出: 长度是: 3
// 这将导致错误:
// let numberChecker = new LengthChecker<number>();
// 类型 'number' 不满足约束 'Lengthwise'.
在泛型类中实现泛型接口
现在,让我们通过在泛型类中实现泛型接口,将我们的技能提升到下一个层次。就像为不同类型的冰淇淋(类)创建一个食谱(接口)!
首先,让我们定义一个泛型接口:
interface Repository<T> {
getById(id: number): T;
save(item: T): void;
}
这个Repository
接口定义了处理数据存储和检索的类的契约。现在,让我们用泛型类实现这个接口:
class GenericRepository<T> implements Repository<T> {
private items: T[] = [];
getById(id: number): T {
return this.items[id];
}
save(item: T): void {
this.items.push(item);
}
}
我们的GenericRepository
类实现了Repository
接口。它可以处理任何类型T
。让我们使用它:
interface User {
name: string;
age: number;
}
let userRepo = new GenericRepository<User>();
userRepo.save({ name: "Alice", age: 30 });
userRepo.save({ name: "Bob", age: 25 });
console.log(userRepo.getById(0)); // 输出: { name: "Alice", age: 30 }
console.log(userRepo.getById(1)); // 输出: { name: "Bob", age: 25 }
在这个例子中,我们创建了一个用于User
对象的仓库。但我们泛型实现的美丽之处在于,我们可以同样轻松地为任何其他类型创建一个仓库!
方法表
以下是总结我们所涵盖方法的便捷表格:
方法 | 描述 | 示例 |
---|---|---|
constructor(value: T) |
创建泛型类的新实例 | new Box<number>(42) |
getValue(): T |
返回泛型类中存储的值 | numberBox.getValue() |
getFirst(): T |
返回一对中的第一个值 | pair.getFirst() |
getSecond(): U |
返回一对中的第二个值 | pair.getSecond() |
checkLength(obj: T): string |
检查对象(有约束)的长度 | stringChecker.checkLength("你好") |
getById(id: number): T |
通过ID从仓库检索项目 | userRepo.getById(0) |
save(item: T): void |
将项目保存到仓库 | userRepo.save({ name: "Alice", age: 30 }) |
就是这样,伙计们!我们一起穿越了TypeScript泛型类的领域,从基本的盒子到复杂的仓库。记住,熟能生巧,所以不要害怕尝试这些概念。谁知道呢?你可能会创造出编程界的下一个大事件!下次见,快乐编码!
Credits: Image by storyset