TypeScript实现调用后移除类方法的解决方案问询
实现调用后自动移除自身的类方法类型约束与运行时限制
问题描述
我需要实现一个类,要求类的方法被调用后,返回的实例类型中自动排除该方法,同时运行时也无法再次调用该方法。以包含step1、step2、step3的Setup类为例:
class Setup { step1() { return this; } step2() { return this; } step3() { return this; } } let setup = new Setup();
具体需求:
- 调用
step1后,返回的实例只能调用step2和step3;调用step2后只能调用step3,以此优化开发体验 - 方法调用顺序不受限制,比如可以先调用
step3再调用step1 - 运行时一旦方法被调用,就无法再次调用,例如:
setup .step1() .step2() .step1(); // 此处应报错,因为step1已经被调用过
我之前尝试用Omit类型实现,但每次都是基于原始Setup类型排除单个方法,导致调用step2后仍然能看到step1可选,没法实现累计排除已调用方法的效果:
export type Omit<A extends object, K extends string> = Pick<A, Exclude<keyof A, K>> class Setup { step1(): Omit<Setup, 'step1'> { return this; } step2(): Omit<Setup, 'step2'>{ return this; } step3():Omit<Setup, 'step3'>{ return this; } } let setup = new Setup();
解决方案
要实现累计排除已调用方法的效果,需要结合TypeScript泛型跟踪已调用方法和运行时动态删除方法两部分:
完整实现代码
type SetupMethods = 'step1' | 'step2' | 'step3'; // 用泛型T跟踪已调用的方法,默认值为空集合 class Setup<T extends SetupMethods = never> { step1(): Omit<Setup<T | 'step1'>, T | 'step1'> { delete (this as any).step1; return this as Omit<Setup<T | 'step1'>, T | 'step1'>; } step2(): Omit<Setup<T | 'step2'>, T | 'step2'> { delete (this as any).step2; return this as Omit<Setup<T | 'step2'>, T | 'step2'>; } step3(): Omit<Setup<T | 'step3'>, T | 'step3'> { delete (this as any).step3; return this as Omit<Setup<T | 'step3'>, T | 'step3'>; } } // 测试示例 const setup = new Setup(); setup.step1().step2().step3(); // 正常链式调用 setup.step1().step3().step2(); // 正常链式调用 setup.step3().step1().step3(); // 类型报错:step3不存在,运行时也会报错 setup.step1().step2().step1(); // 类型报错:step1不存在,运行时也会报错
关键说明
泛型跟踪已调用方法:
- 类的泛型参数
T用来记录已经被调用过的方法名,初始值为never(表示没有方法被调用) - 每个方法返回的类型是
Omit<Setup<T | 当前方法名>, T | 当前方法名>,这样每次调用方法后,返回的实例类型会自动排除所有已调用过的方法(包括当前方法)
- 类的泛型参数
运行时删除方法:
- 在每个方法内部,通过
delete this[方法名]删除实例上的该方法,确保运行时无法再次调用 - 这里用
(this as any)是因为TypeScript会阻止删除类的原型方法,通过类型断言绕过检查(实际运行时操作的是实例自身的属性,不会影响原型)
- 在每个方法内部,通过
类型约束生效:
- TypeScript会根据泛型参数的变化,动态推导返回实例的可用方法,比如调用
step1后,返回的实例类型就不再包含step1,编辑器会自动提示可用方法,同时报错非法调用
- TypeScript会根据泛型参数的变化,动态推导返回实例的可用方法,比如调用
内容的提问来源于stack exchange,提问作者Anirudh Modi
相关产品推荐
相关产品推荐

