TypeScript 4.0+ 能否通过泛型定义可动态扩展服务属性的类型?
实现方案
完全可以通过TypeScript泛型特性实现需求,核心思路是利用泛型参数推导+交叉类型动态生成Device实例的类型,在编译期严格控制可访问的服务属性。
前置约定
首先定义各服务类,以及服务到挂载属性的映射规则:
// 示例服务类 class ControlService { open() { console.log('打开设备') } } class MonitorService { getStatus() { return '运行正常' } } // 约定:服务类 -> 挂载属性名的映射 type ServicePropMap = { ControlService: 'control' MonitorService: 'monitor' } type AllSupportedService = ControlService | MonitorService
类型工具定义
编写工具类型将传入的服务实例元组转换为Device实例上的属性类型:
// 单个服务实例转对应属性对象 type ServiceToProp<T extends AllSupportedService> = T extends { constructor: { name: keyof ServicePropMap } } ? Record<ServicePropMap[T['constructor']['name']], T> : never // 服务实例元组转交叉类型的属性集合 type DeviceServiceProps<T extends AllSupportedService[]> = T[number] extends infer S ? (S extends AllSupportedService ? ServiceToProp<S> : never) : never
Device类实现
通过构造函数类型断言,让实例类型自动合并基础方法和挂载的服务属性:
class _Device<T extends AllSupportedService[] = []> { constructor(...services: T) { // 运行时挂载服务到对应属性 services.forEach(service => { const prop = ServicePropMap[service.constructor.name as keyof ServicePropMap] // @ts-ignore 运行时逻辑安全,无需类型检查 this[prop] = service }) } // Device本身的基础方法 reboot() { console.log('设备重启中') } } // 对外导出的Device类型,自动合并服务属性 export const Device = _Device as unknown as new <T extends AllSupportedService[] = []>(...services: T) => _Device<T> & DeviceServiceProps<T>
效果验证
// 传入ControlService的场景 const device = new Device(new ControlService()) device.control.open() // ✅ 类型正常,编译通过 device.reboot() // ✅ 基础方法正常访问 device.monitor.getStatus() // ❌ 编译报错:属性monitor不存在 // 不传任何服务的场景 const emptyDevice = new Device() emptyDevice.control.open() // ❌ 编译报错:属性control不存在 emptyDevice.reboot() // ✅ 基础方法正常访问
扩展说明
如果不希望依赖类名做映射,也可以给每个服务类添加静态属性mountProp来声明挂载的属性名,调整映射规则即可,使用逻辑完全一致。
内容的提问来源于stack exchange,提问作者Luftzig
相关产品推荐
相关产品推荐

