TypeScript 泛型类与类的泛型约束的实现
作者:烛衔溟
本文主要介绍了TypeScript 泛型类与类的泛型约束的实现,详细讲解泛型类的应用,介绍类级别与方法级别泛型参数的声明与使用,感兴趣的可以了解一下
本文将带你学习如何将泛型应用于类,创建可复用的通用数据结构,以及如何对类的泛型参数施加约束,提升类型安全性。
你将学到:
- 类级别的泛型参数声明
- 泛型类中方法如何使用泛型
- 对泛型参数施加约束(
extends) - 通用数据存储类的设计与实现
- 泛型类与静态成员的关系
一、泛型类的基本定义
泛型类允许在类名后面声明一个或多个类型参数,这些参数可以在类的属性、方法参数和返回值中使用。
class Box<T> {
private content: T;
constructor(value: T) {
this.content = value;
}
get(): T {
return this.content;
}
set(value: T): void {
this.content = value;
}
}
const stringBox = new Box<string>("hello");
const value = stringBox.get(); // 类型为 string
const numberBox = new Box(42); // 类型推断为 Box<number>
1.1 多个泛型参数
类可以声明多个泛型参数。
class Pair<K, V> {
constructor(public key: K, public value: V) {}
getKey(): K {
return this.key;
}
getValue(): V {
return this.value;
}
}
const p = new Pair("name", "Alice");
const key = p.getKey(); // string
const val = p.getValue(); // string
二、泛型约束在类中的应用
使用 extends 对泛型参数施加约束,限制可传入的类型范围。
2.1 约束泛型参数
interface HasId {
id: number;
}
class Repository<T extends HasId> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getById(id: number): T | undefined {
return this.items.find(item => item.id === id);
}
getAll(): T[] {
return [...this.items];
}
}
interface User extends HasId {
name: string;
}
const userRepo = new Repository<User>();
userRepo.add({ id: 1, name: "Alice" });
userRepo.add({ id: 2, name: "Bob" });
const user = userRepo.getById(1);
console.log(user?.name);
2.2 构造函数参数约束
有时需要确保泛型类可以创建新实例,可以约束参数具有构造函数。
class Factory<T> {
constructor(private ctor: new () => T) {}
create(): T {
return new this.ctor();
}
}
class Person {
name = "Unknown";
}
const personFactory = new Factory(Person);
const p = personFactory.create();
console.log(p.name); // "Unknown"
更常见的模式:要求传入的构造函数接受某些参数。
class Creator<T, Args extends any[]> {
constructor(private ctor: new (...args: Args) => T) {}
create(...args: Args): T {
return new this.ctor(...args);
}
}
class Point {
constructor(public x: number, public y: number) {}
}
const pointCreator = new Creator(Point);
const point = pointCreator.create(10, 20);
console.log(point.x, point.y);
三、泛型类中的方法级别泛型
类级别的泛型参数和方法级别的泛型参数可以同时存在,方法级别的泛型参数独立于类。
class Collector<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
// 方法级别泛型,与类泛型无关
merge<U>(other: Collector<U>): Collector<T | U> {
const result = new Collector<T | U>();
this.items.forEach(i => result.add(i));
other.getAll().forEach(i => result.add(i));
return result;
}
getAll(): T[] {
return [...this.items];
}
}
const strings = new Collector<string>();
strings.add("a");
const numbers = new Collector<number>();
numbers.add(1);
const merged = strings.merge(numbers);
const items = merged.getAll(); // (string | number)[]
四、通用数据存储类示例
设计一个带泛型的存储类,支持增删改查,并可扩展约束。
interface Storable {
id: string | number;
}
class DataStore<T extends Storable> {
private store = new Map<T["id"], T>();
save(item: T): void {
this.store.set(item.id, item);
}
find(id: T["id"]): T | undefined {
return this.store.get(id);
}
findAll(): T[] {
return Array.from(this.store.values());
}
update(id: T["id"], updates: Partial<T>): T | undefined {
const existing = this.store.get(id);
if (!existing) return undefined;
const updated = { ...existing, ...updates };
this.store.set(id, updated);
return updated;
}
delete(id: T["id"]): boolean {
return this.store.delete(id);
}
has(id: T["id"]): boolean {
return this.store.has(id);
}
}
// 使用示例
interface Product extends Storable {
id: string;
name: string;
price: number;
}
const productStore = new DataStore<Product>();
productStore.save({ id: "p1", name: "Laptop", price: 999 });
productStore.save({ id: "p2", name: "Mouse", price: 25 });
const product = productStore.find("p1");
console.log(product?.name);
productStore.update("p1", { price: 899 });
const updated = productStore.find("p1");
console.log(updated?.price); // 899
productStore.delete("p2");
console.log(productStore.has("p2")); // false
五、泛型类的默认类型
可以为泛型参数提供默认类型。
class Wrapper<T = string> {
value: T;
constructor(value: T) {
this.value = value;
}
}
const w1 = new Wrapper(42); // Wrapper<number>
const w2 = new Wrapper("hello"); // Wrapper<string>(默认也可)
const w3 = new Wrapper(true); // Wrapper<boolean>
默认类型在扩展类或创建复杂工具类时很有用。
六、泛型类与静态成员
静态成员不能引用类的泛型参数,因为静态成员属于类本身,而泛型参数在实例化时才确定。
class Container<T> {
// static defaultValue: T; // ❌ 错误
static defaultString: string = ""; // OK
}
如果需要泛型相关的静态方法,可以将泛型参数移到方法上。
class Utils {
static wrap<T>(value: T): T[] {
return [value];
}
}
七、常见错误与注意事项
7.1 泛型类不能直接用于类型保护
instanceof 不能用于泛型类擦除后的具体类型。
class Box<T> {}
const b = new Box<number>();
// console.log(b instanceof Box<number>); // ❌ 运行时不存在泛型参数
console.log(b instanceof Box); // OK,但无法区分具体类型
7.2 构造签名与泛型约束
当需要在泛型类中创建实例时,必须约束泛型参数具有构造函数。
class Creator<T> {
// 错误:T 可能没有构造函数
// create(): T { return new T(); }
constructor(private ctor: new () => T) {}
create(): T {
return new this.ctor();
}
}
7.3 泛型类中的类型参数不可用于静态上下文
如前所述,静态成员不能引用泛型参数,因为静态成员在类加载时存在,而泛型参数是实例级别的。
7.4 忽略泛型约束导致运行时错误
如果不在泛型类中对传入的类型做约束,而在方法中假设某些属性存在,可能会在编译时通过但运行时出错。
// 危险示例
class BadStore<T> {
items: T[] = [];
getById(id: number): T | undefined {
// 假设 T 有 id 属性
return this.items.find(item => (item as any).id === id);
}
}
正确做法:使用 extends 约束类型必须有 id 属性。
7.5 泛型类与继承
子类继承泛型父类时,需要提供具体的泛型参数或继承泛型参数。
class Base<T> {
value: T;
constructor(value: T) { this.value = value; }
}
// 具体化泛型参数
class StringChild extends Base<string> {
constructor(value: string) { super(value); }
}
// 保持泛型
class GenericChild<T> extends Base<T> {
getValue(): T {
return this.value;
}
}
八、综合示例
// 定义一个带约束的泛型类:缓存系统
interface Cacheable {
key: string;
expiresAt?: Date;
}
class Cache<T extends Cacheable> {
private storage = new Map<string, { data: T; expireAt?: Date }>();
set(data: T): void {
this.storage.set(data.key, {
data,
expireAt: data.expiresAt
});
}
get(key: string): T | undefined {
const entry = this.storage.get(key);
if (!entry) return undefined;
if (entry.expireAt && entry.expireAt < new Date()) {
this.storage.delete(key);
return undefined;
}
return entry.data;
}
has(key: string): boolean {
const entry = this.storage.get(key);
if (!entry) return false;
if (entry.expireAt && entry.expireAt < new Date()) {
this.storage.delete(key);
return false;
}
return true;
}
clear(): void {
this.storage.clear();
}
// 清理过期条目
cleanup(): void {
for (const [key, entry] of this.storage.entries()) {
if (entry.expireAt && entry.expireAt < new Date()) {
this.storage.delete(key);
}
}
}
}
// 使用示例
interface Session extends Cacheable {
key: string;
userId: number;
token: string;
}
const sessionCache = new Cache<Session>();
sessionCache.set({
key: "session_123",
userId: 1001,
token: "abc123",
expiresAt: new Date(Date.now() + 3600000) // 1小时过期
});
console.log(sessionCache.has("session_123")); // true
const session = sessionCache.get("session_123");
console.log(session?.userId);
// 泛型工厂类
class GenericFactory<T> {
private instances: T[] = [];
constructor(private ctor: new (...args: any[]) => T) {}
create(...args: any[]): T {
const instance = new this.ctor(...args);
this.instances.push(instance);
return instance;
}
getAll(): T[] {
return [...this.instances];
}
}
class Logger {
constructor(public name: string) {}
log(msg: string) {
console.log(`[${this.name}] ${msg}`);
}
}
const loggerFactory = new GenericFactory(Logger);
const logger1 = loggerFactory.create("App");
const logger2 = loggerFactory.create("DB");
logger1.log("start");
logger2.log("connected");
console.log(loggerFactory.getAll().length); // 2
九、小结
| 概念 | 语法/示例 | 说明 |
|---|---|---|
| 泛型类 | class Box<T> { value: T; } | 类级别的类型参数 |
| 多个泛型参数 | class Pair<K, V> { constructor(k: K, v: V) } | 多个类型参数 |
| 泛型约束 | class Repo<T extends HasId> | 限制类型必须满足特定形状 |
| 方法级别泛型 | merge<U>(other: Collector<U>) | 方法可以有自己的泛型参数 |
| 默认泛型参数 | class Container<T = string> | 提供默认类型 |
| 静态成员限制 | 静态成员不能使用泛型参数 | 因为静态与实例无关 |
| 构造函数约束 | class Creator<T> { constructor(private ctor: new () => T) {} } | 确保可以创建实例 |
到此这篇关于TypeScript 泛型类与类的泛型约束的实现的文章就介绍到这了,更多相关TypeScript 泛型类与类的泛型约束内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
