When dealing with non-generic classes, the use of Tuples in rest parameters and spread expressions and InstanceType
can allow us to map the constructor to a similar function in TypeScript version 3.0.
However, for generic classes, preserving the type argument during mapping can be a challenge. One solution is to include a field in the class that informs the generate
function about the desired result type. This can be achieved through interface-class merging, enabling the original class to remain unaware of the generate method.
An effective approach that combines both automatic and manual techniques may resemble the following example:
class item<T> {
constructor(a: T) {
this.a = a;
}
a: T
}
const generateResult = Symbol.for("generate")
interface item<T> {
[generateResult] : <T>(a: T) => item<T>
}
type d = item<any>[typeof generateResult]
type SupportsGeneration<R> = { [generateResult] : R }
type ConstructorArguments<T> = T extends new (...a:infer A ) => any ? A : [];
function generate<T extends { new (...a:any[]) : SupportsGeneration<any> }>(c:T) : InstanceType<T> extends SupportsGeneration<infer R> ? R: never
function generate<T extends new (...a:any[]) => any>(c:T) : (a: ConstructorArguments<T>) => InstanceType<T>
function generate(c: new (...a: any[]) => any) : any {
return function a(...a: any[]) {
return new c(...a);
}
}
const factory = generate(item);
const instance = factory('string');