You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何实现泛型类类型参数的部分擦除约束?

结论

当前正式版TypeScript(截至5.4版本)不原生支持你示例中的通配符上下界泛型语法,不过可以通过现有特性模拟需求,官方也有对应特性提案在推进中。

现有可行实现方案

方案1:函数泛型参数约束

不需要虚构语法,直接给函数新增一个泛型参数表达上下界约束,TypeScript可自动推导泛型类型,调用时无需手动传参:

可读场景实现

// 用T extends U表达T是U的子类型,等价于你示例的MyStorage<? extends U>
function readsFromStorage<U, T extends U>(arg: U, storage: MyStorage<T>) {
  const value: U = storage.get('mykey'); // 合法,T类型兼容U
  storage.set('mykey', arg); // 编译报错:U类型无法赋值给更窄的T类型
}

可写场景实现

// 用U extends T表达T是U的父类型,等价于你示例的MyStorage<U extends ?>
function writesToStorage<U, T>(arg: U, storage: MyStorage<T> & (U extends T ? unknown : never)) {
  storage.set('mykey', arg); // 合法,U类型兼容父类型T
  const value: U = storage.get('mykey'); // 编译报错:更宽泛的T类型无法赋值给U
}

方案2:拆分读写接口+声明点形变

TS 4.7及以上版本支持声明点协变/逆变标记,你可以将存储类的读写能力拆分到独立接口,从根源上避免非法操作:

// out标记表示该接口对T是协变的,支持MyStorage<T extends U>赋值给ReadableStorage<U>
interface ReadableStorage<out T> {
  get(key: string): T;
}
// in标记表示该接口对T是逆变的,支持MyStorage<T super U>赋值给WritableStorage<U>
interface WritableStorage<in T> {
  set(key: string, value: T): void;
}
class MyStorage<T> implements ReadableStorage<T>, WritableStorage<T> {
  get(key: string): T { /* 原有实现 */ }
  set(key: string, value: T) { /* 原有实现 */ }
}

使用时直接用拆分后的接口即可自动满足类型约束:

// 只读场景,自动允许所有T extends U的MyStorage实例传入
function readsFromStorage<U>(arg: U, storage: ReadableStorage<U>) {
  const value: U = storage.get('mykey'); // 合法
  storage.set('mykey', arg); // 编译报错:ReadableStorage没有set方法
}
// 只写场景,自动允许所有T是U父类型的MyStorage实例传入
function writesToStorage<U>(arg: U, storage: WritableStorage<U>) {
  storage.set('mykey', arg); // 合法
  const value: U = storage.get('mykey'); // 编译报错:WritableStorage没有get方法
}

官方提案进展

你需要的「类型参数擦除+上下界约束」能力对应TypeScript的存在类型(Existential Types) 提案,该提案目前处于开放状态,规划覆盖通配符泛型、存在类型声明等能力,暂时没有明确的稳定版落地排期。

内容的提问来源于stack exchange,提问作者rkjnsn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 03:45:03