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
相关产品推荐
相关产品推荐

