TypeScript 泛型类与类的泛型约束的实现

 更新时间:2026年06月07日 10:10:01   作者:烛衔溟  
本文主要介绍了TypeScript 泛型类与类的泛型约束的实现,详细讲解泛型类的应用,介绍类级别与方法级别泛型参数的声明与使用,感兴趣的可以了解一下

本文将带你学习如何将泛型应用于类,创建可复用的通用数据结构,以及如何对类的泛型参数施加约束,提升类型安全性。

你将学到:

  1. 类级别的泛型参数声明
  2. 泛型类中方法如何使用泛型
  3. 对泛型参数施加约束(extends
  4. 通用数据存储类的设计与实现
  5. 泛型类与静态成员的关系

一、泛型类的基本定义

泛型类允许在类名后面声明一个或多个类型参数,这些参数可以在类的属性、方法参数和返回值中使用。

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 泛型类与类的泛型约束内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 通过JS深度判断两个对象字段相同

    通过JS深度判断两个对象字段相同

    这篇文章主要介绍了通过JS深度判断两个对象字段相同,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06
  • js实现文字无缝向上滚动

    js实现文字无缝向上滚动

    本文主要分享了js实现文字无缝向上滚动的示例代码,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • Zepto实现密码的隐藏/显示

    Zepto实现密码的隐藏/显示

    本文主要介绍了js中隐藏/显示密码的Zepto的相关知识。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-04-04
  • 微信小程序按钮去除边框线分享页面功能

    微信小程序按钮去除边框线分享页面功能

    这篇文章主要介绍了微信小程序按钮去除边框线分享页面功能,文中通过一段简单的代码给大家介绍了微信小程序的button去边框的方法,感兴趣的朋友跟随脚本之家小编一起看看吧
    2018-08-08
  • js TextArea的选中区域处理

    js TextArea的选中区域处理

    js中对于TextArea的选中区域后进行处理的代码,需要的朋友可以参考下。
    2010-12-12
  • JavaScript

    JavaScript"模拟事件"的注意要点详解

    今天小编就为大家分享一篇关于JavaScript"模拟事件"的注意要点详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02
  • 纯javascript实现的小游戏《Flappy Pig》实例

    纯javascript实现的小游戏《Flappy Pig》实例

    这篇文章主要介绍了纯javascript实现的小游戏《Flappy Pig》,较为详细的分析了javascript实现小游戏《Flappy Pig》的相关技巧,涉及javascript操作页面元素移动、碰撞、事件监听与触发的相关技巧,需要的朋友可以参考下
    2015-07-07
  • 原生js实现移动端Touch轮播图的方法步骤

    原生js实现移动端Touch轮播图的方法步骤

    这篇文章主要介绍了原生js实现移动端Touch轮播图的方法步骤,touch轮播图其实就是通过手指的滑动,来左右切换轮播图,touch轮播图其实就是通过手指的滑动,来左右切换轮播图,
    2019-01-01
  • Bootstrap carousel轮转图的使用实例详解

    Bootstrap carousel轮转图的使用实例详解

    图片轮播效果在Web中常常能看到,很多人也称之为幻灯片。这篇文章主要给大家介绍Bootstrap carousel轮转图的使用实例详解,需要的朋友可以参考下
    2016-05-05
  • Bootstrap datepicker日期选择器插件使用详解

    Bootstrap datepicker日期选择器插件使用详解

    这篇文章主要为大家详细介绍了Bootstrap datepicker日期选择器插件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07

最新评论