TypeScript多泛型方法模式:类方法自由组合实现方案问询
TypeScript 泛型实现Person方法的自由组合
要实现不用子类、仅通过泛型参数指定方法实现的组合需求,可以通过标记类型映射+泛型类的方式优雅解决,编译时即可确定类型,实例化时无需传递函数参数,具体实现如下:
1. 定义实现标记与映射关系
先为每个方法的不同实现定义唯一的类型标记,再创建标记到具体实现的映射:
// 定义getFirstName的实现类型标记 type FirstNameTag = "Alice" | "Bob"; // 定义getSurname的实现类型标记 type SurnameTag = "Smith" | "Jones"; // 方法实现的类型约束 type FirstNameProvider = { getFirstName: () => string }; type SurnameProvider = { getSurname: () => string }; // 标记到具体实现的映射 const FirstNameMap: Record<FirstNameTag, FirstNameProvider> = { Alice: { getFirstName: () => "Alice" }, Bob: { getFirstName: () => "Bob" }, }; const SurnameMap: Record<SurnameTag, SurnameProvider> = { Smith: { getSurname: () => "Smith" }, Jones: { getSurname: () => "Jones" }, };
2. 泛型Person类实现
创建泛型类,通过泛型参数接收实现标记,直接从映射中获取对应方法实现并绑定到类:
class Person<F extends FirstNameTag, S extends SurnameTag> implements FirstNameProvider, SurnameProvider { // 直接从映射中获取对应实现,编译时会检查标记的合法性 getFirstName = FirstNameMap[F].getFirstName; getSurname = SurnameMap[S].getSurname; }
3. 实例化与使用
实例化时仅需指定泛型参数,无需传递任何函数参数,所有组合都可自由生成:
// 生成Alice Smith的实例 const aliceSmith = new Person<"Alice", "Smith">(); console.log(aliceSmith.getFirstName()); // 输出: Alice console.log(aliceSmith.getSurname()); // 输出: Smith // 生成Bob Jones的实例 const bobJones = new Person<"Bob", "Jones">(); console.log(bobJones.getFirstName()); // 输出: Bob console.log(bobJones.getSurname()); // 输出: Jones
扩展:支持有状态的方法实现
如果方法需要依赖实例状态,可调整类的实现,确保方法绑定到实例上下文:
// 带状态的FirstName实现 type StatefulFirstNameProvider = FirstNameProvider & { prefix: string }; const StatefulFirstNameMap: Record<FirstNameTag, StatefulFirstNameProvider> = { Alice: { prefix: "Ms.", getFirstName() { return `${this.prefix} Alice`; }, }, Bob: { prefix: "Mr.", getFirstName() { return `${this.prefix} Bob`; }, }, }; class StatefulPerson<F extends FirstNameTag, S extends SurnameTag> implements FirstNameProvider, SurnameProvider { private firstNameImpl: StatefulFirstNameProvider; private surnameImpl: SurnameProvider; constructor() { // 克隆实现对象,避免实例间状态污染 this.firstNameImpl = { ...StatefulFirstNameMap[F] }; this.surnameImpl = { ...SurnameMap[S] }; // 绑定方法到当前实例 this.getFirstName = this.getFirstName.bind(this); } getFirstName(): string { return this.firstNameImpl.getFirstName.call(this); } getSurname(): string { return this.surnameImpl.getSurname(); } // 允许修改实例状态 setPrefix(prefix: string) { this.firstNameImpl.prefix = prefix; } } // 使用示例 const alice = new StatefulPerson<"Alice", "Smith">(); console.log(alice.getFirstName()); // 输出: Ms. Alice alice.setPrefix("Dr."); console.log(alice.getFirstName()); // 输出: Dr. Alice
该方案的优势:
- 避免了为每种组合创建子类,新增实现仅需添加标记和对应映射项
- 泛型参数在编译时会进行类型校验,确保指定的实现标记合法
- 实例化过程简洁,仅需指定泛型参数,无需传递额外参数
内容的提问来源于stack exchange,提问作者Morgoth
相关产品推荐
相关产品推荐

