Reputation: 7823
Here is some set up code:
// This is from lib.d.ts
type ConstructorParameters<T extends new (...args: any[]) => any>
= T extends new (...args: infer P) => any ? P : never
// Represents a type with a constructor function
type Newable<TType> = {
new(...params: any[]): TType;
};
// A sample class
class Foo {
constructor(
options: { bar: string }
) {
}
public bar() { }
}
Here is a generic class factory that I'd like to type but I'm failing at the moment:
// Error: 'T' refers only to a type, but is being used as a value here
declare function create<T>(kind: Newable<T>, options?:
ConstructorParameters<typeof T>[0]): T;
I want to use it like this:
var x = create(Foo, { bar2: 'ss' }); // This should fail because the constructor option is wrong
x.bar();
I understand the error, but does anyone know how to get this working?
Upvotes: 1
Views: 270
Reputation: 7823
Alternative solution, closer to the original snippet:
type Newable<TType extends new(...args:any[]) => InstanceType<TType>> = {
new(...params: any[]): InstanceType<TType>;
};
// A sample class
class Foo {
constructor(
options: { bar: string }
) {
}
public bar() { }
}
declare function create<T extends Newable<T>>(kind: T, options?:
ConstructorParameters<T>[0]): InstanceType<T>;
var x = create(Foo, { bar2: 'ss' }); // Error as expected
x.bar();
Upvotes: 0
Reputation: 250366
There are several problems in your code the bigest one is that you can't do typeof T
, you will need the type parameter to reresent the class not an instance of the class. If T
is the class you can use InstanceType
to get the instance type of the class. You can aslo use a conditional type to extarct the first prameter of the constructor. Also since you want the constructor to always have one parameter, I would not use the general new(...params: any[]): TType;
better to create a custom type to repesent a constrcutor with just one argument type CtorWithOptions<TOpt extends object, T> = new (o: TOpt)=> T
.
Putting it all together:
// A sample class
class Foo {
constructor(
options: { bar: string }
) {
}
public bar() { }
}
type CtorWithOptions<TOpt extends object, T> = new (o: TOpt)=> T
type OptionsFromConstructor<T> = T extends CtorWithOptions<infer TOpt, any> ? TOpt : never;
declare function create<T extends CtorWithOptions<any, any>>(kind: T, options: OptionsFromConstructor<T>): InstanceType<T> {
return new kind(options);
}
var x = create(Foo, { bar2: 'ss' }); // Fails
var x = create(Foo, { bar: 'ss' }); // OK
x.bar();
Upvotes: 1