如何实现泛型接口实现类的类型安全使用?
解决方案
要实现你需要的类型安全,核心是让工厂函数的返回类型与输入参数强关联,同时保留每个实现类的泛型约束。可以通过泛型映射来达成目标:
1. 定义类型映射
先创建一个类型,把输入的数字和对应的具体服务类绑定,让TS能明确参数和返回值的对应关系:
type ServiceMap = { 1: ConcreteOne; 2: ConcreteTwo; };
2. 改造工厂函数为泛型函数
让getConcrete接收泛型参数,对应输入的数字类型,返回映射中对应的服务类:
function getConcrete<T extends keyof ServiceMap>(number: T): ServiceMap[T] { if(number === 1) { return new ConcreteOne() as ServiceMap[T]; } if(number === 2) { return new ConcreteTwo() as ServiceMap[T]; } throw new Error("Invalid service number"); }
这里的类型断言是安全的——我们明确知道分支返回值和泛型的对应关系,TS只是无法自动推断这一点。
3. 验证类型安全效果
现在主逻辑里的类型会自动关联,同时跨实例传参的错误会被TS捕获:
// 正确用法:同一实例传参,无类型错误 function mainFunction(number: 1 | 2) { const concrete = getConcrete(number); const data = concrete.getData(); concrete.useData(data); } // 错误用法:跨实例传参触发类型错误 function crossInstanceTest() { const concrete1 = getConcrete(1); const concrete2 = getConcrete(2); const data = concrete1.getData(); concrete2.useData(data); // TS错误:类型"{ foo: string; }"的参数不能赋给类型"{ foo: number; }"的参数 }
方案优势
- 工厂函数的返回类型不再是模糊的联合类型,而是和输入参数一一对应的具体类型,TS能完整跟踪每个实例的真实类型。
- 同一实例的
getData()返回值和useData()参数类型完全匹配,保证类型安全。 - 不同实例之间的类型天然不兼容,跨实例传参时会直接触发类型错误,符合你的需求。
完整代码示例
interface GenericService<Data extends {}> { getData(): Data; useData(data: Data): Data; } class ConcreteOne implements GenericService<{ foo: string }> { getData() { return { foo: 'bar' }; } useData(data: { foo: string }): { foo: string } { return data; } } class ConcreteTwo implements GenericService<{ foo: number }> { getData() { return { foo: 1 }; } useData(data: { foo: number }): { foo: number } { return data; } } type ServiceMap = { 1: ConcreteOne; 2: ConcreteTwo; }; function getConcrete<T extends keyof ServiceMap>(number: T): ServiceMap[T] { if(number === 1) { return new ConcreteOne() as ServiceMap[T]; } if(number === 2) { return new ConcreteTwo() as ServiceMap[T]; } throw new Error("Invalid service number"); } // 正确用法示例 function mainFunction(number: 1 | 2) { const concrete = getConcrete(number); const data = concrete.getData(); concrete.useData(data); } // 错误用法示例(触发类型错误) function crossInstanceTest() { const concrete1 = getConcrete(1); const concrete2 = getConcrete(2); const data = concrete1.getData(); concrete2.useData(data); }
内容的提问来源于stack exchange,提问作者Jack Butler
相关产品推荐
相关产品推荐

