如何复用底层库类型,为链式调用包装类推断方法签名?
Absolutely! You can absolutely reuse and extend the type definitions from your underlying library to get full type inference for your chainable wrapper. Here's how to modify your implementation to make TypeScript aware of all the library methods and their signatures:
First, we'll extract the full type of your library so we can reference it throughout our chainable class:
import lib from 'lib'; // Get the complete type definition of the library type Lib = typeof lib;
We'll create a generic type that transforms each method in the library into a chainable version. This type will automatically adapt to the library's method signatures, removing the need to manually pass the current value as the first argument:
// Transform library methods into chainable equivalents type ChainableMethods<T> = { [MethodName in keyof Lib]: Lib[MethodName] extends (input: T, ...args: infer ExtraArgs) => infer ReturnValue ? (...args: ExtraArgs) => Chainable<ReturnValue> : never; };
What this does:
- For every method in the library, we check if it accepts the current chainable
valuetype (T) as its first argument - If it does, we create a new method signature that accepts only the remaining arguments of the original method
- The new method returns an updated
Chainableinstance with the return type of the original method as its newvaluetype
We'll turn your original Chainable class into a generic class that implements our mapped ChainableMethods<T> interface. This tells TypeScript about all the dynamically added methods:
class Chainable<T> implements ChainableMethods<T> { public value: T; constructor(value: T) { this.value = value; } }
We'll retain your original dynamic method injection logic, but add type assertions to keep TypeScript happy:
// Dynamically attach all library methods to the Chainable prototype const methods = Object.keys(lib) as Array<keyof Lib>; for (const method of methods) { Chainable.prototype[method] = function(this: Chainable<any>, ...args: any[]) { this.value = Reflect.apply(lib[method], null, [this.value, ...args]); return this; }; } export default Chainable;
Now when you use your Chainable class, TypeScript will automatically:
- Validate arguments for every library method
- Infer the correct type of
valueafter each method call - Prevent calls to methods that don't accept the current
valuetype
Example usage:
// TypeScript will enforce lib.one's signature, infer the next value type, and so on const result = new Chainable('abc') .one() .two(/* If lib.two requires extra args, TypeScript will enforce them here */) .three() .value; // Type is automatically set to the return type of lib.three
Bonus: Handling Method Overloads
This implementation works seamlessly with library methods that have overloads—TypeScript will recognize all valid overload signatures for each chainable method.
内容的提问来源于stack exchange,提问作者i_like_robots

