TypeScript中如何复用多函数重载?重复重载代码优化求助
复用TypeScript函数重载定义的解决方案
你遇到的这个重复重载维护问题确实很头疼——每次修改都要同步两个地方,很容易出错。好在TypeScript有几种方式可以帮你复用这些重载逻辑,不用重复写一堆相同的泛型签名。
方法1:提取重载到类型别名,在类中复用
首先,我们可以把所有slice的重载签名提取成一个函数类型别名,这样两个类都能直接引用这个类型,不用重复定义:
// 先定义通用的slice重载类型,S是原对象的类型 type SliceOverloads<S> = { <Key extends keyof S>(key: Key): Slice<S[Key], S>; <Key1 extends keyof S, Key2 extends keyof S[Key1]>(key1: Key1, key2: Key2): Slice<S[Key1][Key2], S>; <Key1 extends keyof S, Key2 extends keyof S[Key1], Key3 extends keyof S[Key1][Key2]>(key1: Key1, key2: Key2, key3: Key3): Slice<S[Key1][Key2][Key3], S>; <Key1 extends keyof S, Key2 extends keyof S[Key1], Key3 extends keyof S[Key1][Key2], Key4 extends keyof S[Key1][Key2][Key3]>(key1: Key1, key2: Key2, key3: Key3, key4: Key4): Slice<S[Key1][Key2][Key3][Key4], S>; <Key1 extends keyof S, Key2 extends keyof S[Key1], Key3 extends keyof S[Key1][Key2], Key4 extends keyof S[Key1][Key2][Key3], Key5 extends keyof S[Key1][Key2][Key3][Key4]>(key1: Key1, key2: Key2, key3: Key3, key4: Key4, key5: Key5): Slice<S[Key1][Key2][Key3][Key4][Key5], S>; <Key1 extends keyof S, Key2 extends keyof S[Key1], Key3 extends keyof S[Key1][Key2], Key4 extends keyof S[Key1][Key2][Key3], Key5 extends keyof S[Key1][Key2][Key3][Key4], Key6 extends keyof S[Key1][Key2][Key3][Key4][Key5]>(key1: Key1, key2: Key2, key3: Key3, key4: Key4, key5: Key5, key6: Key6): Slice<S[Key1][Key2][Key3][Key4][Key5][Key6], S>; <Key1 extends keyof S, Key2 extends keyof S[Key1], Key3 extends keyof S[Key1][Key2], Key4 extends keyof S[Key1][Key2][Key3], Key5 extends keyof S[Key1][Key2][Key3][Key4], Key6 extends keyof S[Key1][Key2][Key3][Key4][Key5], Key7 extends keyof S[Key1][Key2][Key3][Key4][Key5][Key6]>(key1: Key1, key2: Key2, key3: Key3, key4: Key4, key5: Key5, key6: Key6, key7: Key7): Slice<S[Key1][Key2][Key3][Key4][Key5][Key6][Key7], S>; <Key1 extends keyof S, Key2 extends keyof S[Key1], Key3 extends keyof S[Key1][Key2], Key4 extends keyof S[Key1][Key2][Key3], Key5 extends keyof S[Key1][Key2][Key3][Key4], Key6 extends keyof S[Key1][Key2][Key3][Key4][Key5], Key7 extends keyof S[Key1][Key2][Key3][Key4][Key5][Key6], Key8 extends keyof S[Key1][Key2][Key3][Key4][Key5][Key6][Key7]>(key1: Key1, key2: Key2, key3: Key3, key4: Key4, key5: Key5, key6: Key6, key7: Key7, key8: Key8): Slice<S[Key1][Key2][Key3][Key4][Key5][Key6][Key7][Key8], S>; (...keys: string[]): any; };
然后在你的Atom类(以及另一个需要的类)里,直接用这个类型来定义slice方法,只需要实现一次方法体:
export class Atom<S> { constructor(private initial: S) { } // 直接引用预定义的重载类型 slice: SliceOverloads<S> = (...keys: string[]) => { return new Slice(this, keys); }; }
这样一来,所有的重载逻辑都集中在SliceOverloads里,后续修改只需要改这一个地方,所有引用它的类都会自动更新类型。
方法2:用接口实现重载复用
另一种方式是定义一个包含slice重载的接口,让需要的类实现这个接口。这种方式更符合面向对象的设计思路:
// 定义接口,包含所有slice重载 interface HasSlice<S> { slice<Key extends keyof S>(key: Key): Slice<S[Key], S>; slice<Key1 extends keyof S, Key2 extends keyof S[Key1]>(key1: Key1, key2: Key2): Slice<S[Key1][Key2], S>; // ... 其余重载和上面一致 slice(...keys: string[]): any; } // 让Atom类实现这个接口 export class Atom<S> implements HasSlice<S> { constructor(private initial: S) { } // 只需要实现一次方法体,重载签名由接口自动提供 slice(...keys: string[]) { return new Slice(this, keys); } }
这种方式的好处是,你可以强制所有需要slice方法的类遵循相同的重载规则,而且修改接口就能同步所有实现类的类型。
为什么你之前的尝试丢失了类型推导?
你之前把slice抽成独立函数,但类里的方法没有保留重载签名——TypeScript只会识别最后那个实现签名的类型,所以丢失了之前的泛型推导能力。上面两种方法都是把重载签名集中定义,让类直接复用这些签名,从而保留完整的类型推导。
内容的提问来源于stack exchange,提问作者dagda1
相关产品推荐
相关产品推荐

