Issue
Given a function that returns a factory, how can I annotate the function/factory so that it contains the correct type definitions?
Here's my example:
class item<T> {
constructor(a: T) {
this.a = a;
}
a: T
}
function generate(c) {
return function a(a) {
return new c(a);
}
}
const factory = generate(item); // I want this to always be annotated as <T> (a: T) => item<T>
const instance = factory('string'); // instance should now be of type item<string>
Is this possible in typescript or should I suggest it as a new feature?
Solution
For non generic classes we can in 3.0 use Tuples in rest parameters and spread expressions and InstanceType
to map the constructor to a similar function.
For generic classes unfortunately there is no way to preserve the type argument when mapping. The only solution is to add a field to the class that will tell generate
what the result type should be. This can be done using interface-class merging so the original class does not know about generate.
A possible solution using both approaches (automatic where possible, manual where necessary) could look something like this:
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');
Answered By - Titian Cernicova-Dragomir
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.