Below is the solution for version 3.0
If you need to change the return type of a function, you can follow a similar approach as shown in this example
type TProxyify<T> = {
[K in keyof T]: AddReturnType<T[K], string>;
};
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type AddReturnType<T, TNewReturn> = T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F,
g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => R | TNewReturn :
IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => R | TNewReturn :
IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => R | TNewReturn :
IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => R | TNewReturn :
IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => R | TNewReturn :
IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => R | TNewReturn :
IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => R | TNewReturn :
IsValidArg<C> extends true ? (a: A, b: B, c: C) => R | TNewReturn :
IsValidArg<B> extends true ? (a: A, b: B) => R | TNewReturn :
IsValidArg<A> extends true ? (a: A) => R | TNewReturn :
() => R | TNewReturn
) : T
export function wrapType<T>(): TProxyify<T> {
return 1 as any;
}
interface IBaseInterface {
test(a?: boolean, b?: number): Promise<boolean>;
anotherTest?(a?: number): Promise<number>;
}
const s = wrapType<IBaseInterface>();
let ss = s.test(undefined, undefined); // result will be string | Promise<boolean>
An important point to note about this method is that optional parameters become required when used, which results in them being of type A or undefined. Hence, the call to test is s.test(undefined, undefined);
rather than s.test();
Furthermore, parameter names are not retained, potentially impacting code readability.
Edit
Subsequent to providing an initial response, TypeScript has introduced a more efficient solution to this issue. With the inclusion of Tuples in rest parameters and spread expressions, there is no longer a need for multiple overloads:
type TProxyify<T> = {
[K in keyof T]: AddReturnType<T[K], string>;
};
type ArgumentTypes<T> = T extends (... args: infer U ) => any ? U: never;
type AddReturnType<T, TNewReturn> = T extends (...args: any[])=> infer R ? (...a: ArgumentTypes<T>) => TNewReturn | R : T;
export function wrapType<T>(): TProxyify<T> {
return 1 as any;
}
interface IBaseInterface {
test(a?: boolean, b?: number): Promise<boolean>;
anotherTest?(a?: number): Promise<number>;
}
const s = wrapType<IBaseInterface>();
let ss = s.test(undefined, undefined); // result will be string | Promise<boolean>
This revised approach is concise and addresses various issues:
- Optional parameters stay optional
- Parameter names are preserved
- Works effectively with any quantity of arguments