TypeScript中基于接口实现通用工厂方法的可行性问询
TypeScript接口实现通用get泛型方法的可行性问题
我有一组由OpenAPI Generator自动生成的TypeScript接口,希望实现如下示例的泛型方法:
interface Foo {} function getFoo(id: string): Foo { /* ... */ } interface Bar {} function getBar(id: string): Bar { /* ... */ } function get<T>(type: Type<T>, id: string): T { switch(type) { case typeof Foo: return getFoo(id); case typeof Bar: return getBar(id); default: throw "Unknown type"; } }
请问这种实现方式是否可行?
我尝试过两种方案,但都有问题:
- 若Foo和Bar为类,可以用以下方式实现,但该方式不适用于接口:
function get<T>(type: new() => T, id: string): T { switch(typeof new type()){ // ... } } - 给接口添加标签,但这种方式无法避免
get<Bar>('foo', id)这类类型不匹配的调用:interface Foo { type: 'foo'; } interface Bar { type: 'bar'; } function get<T>(type: string, id: string): T { switch(type) { case 'foo': return getFoo(id); case 'bar': return getBar(id); default: throw "Unknown type"; } }
明确结论:原示例的实现方式不可行
TypeScript的接口是编译时专属类型,编译为JavaScript后会被完全擦除,不存在对应的运行时对象,因此typeof Foo这种写法本身就会报错——运行时根本没有Foo这个值,自然无法在switch语句中做判断。
可行的解决方案
方案1:类型映射+字面量约束,保障类型安全
通过定义类型映射表,约束类型标识与返回值类型的对应关系,从根源上避免类型不匹配的调用:
// 定义类型标识与接口的映射关系 type TypeMap = { foo: Foo; bar: Bar; }; // 泛型方法:K必须是TypeMap的键,返回对应接口类型 function get<K extends keyof TypeMap>(type: K, id: string): TypeMap[K] { switch (type) { case 'foo': return getFoo(id) as TypeMap[K]; case 'bar': return getBar(id) as TypeMap[K]; default: throw new Error(`Unknown type: ${type}`); } }
调用时,get('foo', id)会自动推导返回Foo类型,get('bar', id)返回Bar类型,编译器会直接拦截非强制转换的类型不匹配调用。
方案2:枚举作为类型标识,提升可读性
如果觉得字符串字面量不够直观,可以用枚举替代:
enum DataType { Foo = 'foo', Bar = 'bar' } // 关联枚举与接口的映射 type TypeMap = { [DataType.Foo]: Foo; [DataType.Bar]: Bar; }; function get<K extends keyof TypeMap>(type: K, id: string): TypeMap[K] { switch (type) { case DataType.Foo: return getFoo(id) as TypeMap[K]; case DataType.Bar: return getBar(id) as TypeMap[K]; default: throw new Error(`Unknown type: ${type}`); } }
调用时使用get(DataType.Foo, id),既能保证类型安全,代码可读性也更强。
内容的提问来源于stack exchange,提问作者Rasmus Faber
相关产品推荐
相关产品推荐

