See below for 3.0 answer
You can use a similar approach to this answer. You will need to replace the return type of the constructor, and use a mapped type to create new functions that omit the first argument:
type RemoveFirstArg<TCtor extends new (... args: any[]) => any > = ReplaceInstanceType<TCtor, { [P in keyof InstanceType<TCtor>]: RemoveArg<InstanceType<TCtor>[P]> }>
function createNewClass<TCtor extends new (... args: any[]) => any >(aCtor: TCtor) : RemoveFirstArg<TCtor>{
// load all of the A method and remove first params
return null as any;
}
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type RemoveArg<T> = 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 ? (b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => R :
IsValidArg<I> extends true ? (b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => R :
IsValidArg<H> extends true ? (b: B, c: C, d: D, e: E, f: F, g: G, h: H) => R :
IsValidArg<G> extends true ? (b: B, c: C, d: D, e: E, f: F, g: G) => R :
IsValidArg<F> extends true ? (b: B, c: C, d: D, e: E, f: F) => R :
IsValidArg<E> extends true ? (b: B, c: C, d: D, e: E) => R :
IsValidArg<D> extends true ? (b: B, c: C, d: D) => R :
IsValidArg<C> extends true ? (b: B, c: C) => R :
IsValidArg<B> extends true ? (b: B) => R :
IsValidArg<A> extends true ? () => R :
T
) : never
type ReplaceInstanceType<T, TNewReturn> = T extends new (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 ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => TNewReturn :
IsValidArg<I> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => TNewReturn :
IsValidArg<H> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => TNewReturn :
IsValidArg<G> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => TNewReturn :
IsValidArg<F> extends true ? new (a: A, b: B, c: C, d: D, e: E, f: F) => TNewReturn :
IsValidArg<E> extends true ? new (a: A, b: B, c: C, d: D, e: E) => TNewReturn :
IsValidArg<D> extends true ? new (a: A, b: B, c: C, d: D) => TNewReturn :
IsValidArg<C> extends true ? new (a: A, b: B, c: C) => TNewReturn :
IsValidArg<B> extends true ? new (a: A, b: B) => TNewReturn :
IsValidArg<A> extends true ? new (a: A) => TNewReturn :
new () => TNewReturn
) : never
//Usage
class A {
public aCall(a: any, payload: string) { }
public bCall(a: any, payload: number) { }
}
// Extending a class
class C extends createNewClass(A) { }
new C().aCall('xxx')
//For interfaces we can just use the type
interface IC extends RemoveFirstArg<typeof A> { }
Note
The reason for the many, many similar lines is that we need to remap each constructor/function with a specific number of arguments. The implementation above works for 10 arguments, which should be enough but more can be added.
Edit
Since the original question was answered typescript has improved the possible solution to this problem. With the addition of Tuples in rest parameters and spread expressions we now don't need to have all the overloads for RemoveArg
and ReplaceInstanceType
:
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type ArgumentTypes<T> = T extends (... args: infer U ) => any ? U: never;
type ReplaceInstanceType<T, TNewInstance> = T extends new (...args: any[])=> infer R ? new (...a: ArgumentTypes<T>) => TNewInstance : never;
type ArgumentTypesSkipOne<T> = T extends (a: any, ... args: infer U ) => any ? U: never;
type RemoveArg<T> = T extends (a: any, ...args: any[])=> infer R ? (...a: ArgumentTypesSkipOne<T>) => R : T;
type RemoveFirstArg<TCtor extends new (... args: any[]) => any > = ReplaceInstanceType<TCtor, { [P in keyof InstanceType<TCtor>]: RemoveArg<InstanceType<TCtor>[P]> }>
function createNewClass<TCtor extends new (... args: any[]) => any >(aCtor: TCtor) : RemoveFirstArg<TCtor>{
// load all of the A method and remove first params
return null as any;
}
Not only is this shorter but it solves a number of problems
- Optional parameters remain optional
- Argument names are preserved
- Works for any number of arguments