I have a generic class that defines a make
method, mostly as a convenience to avoid having to write new Foo()
. It works like this:
class Parent<T> {
public declare props: T;
constructor(props: T){
this.props = props;
}
static make<T extends typeof Parent<P>, P = InstanceType<T>['props']>(this: T, props: P): InstanceType<T> {
return new this(props) as InstanceType<T>;
}
}
type Props = {
name: string;
}
class Child extends Parent<Props> {
public sayName(){
console.log(this.props.name);
}
}
const instance = Child.make({
name: "Bob"
});
instance.sayName(); // "Bob"
This is almost right. The type of instance
is the correct resolved class (Child
), and IDE type-hinting for the props works as well. However, the only thing that doesn't get done is validating the props passed into make
method. Since the props
parameter is defaulted to InstanceType<T>['props']
, the compiler will not mark missing props as an error. IE, if I left out the name
prop, it wouldn't tell me that "name
is missing on type Props
". I've been playing with this generic concept for some time and I haven't been able to figure out how to get this last piece of the puzzle - or even if it is at all possible.
Does anyone have insight into how the props can be validated properly?
Don't mix two parameters C
for the constructor type and P
for the parameter. Either
make the method generic only over the constructor type, and get its parameter type with ConstructorParameters
:
static make<C extends typeof Parent<unknown>>(this: C, props: Co nstructorParameters<C>[0]): InstanceType<C> {
return new this(props) as InstanceType<C>;
}
or (simpler/shorter and avoiding the type assertion) use two generic types for the parameter and the resulting instance:
static make<P, T>(this: {new (p: P): T}, props: P): T {
return new this(props);
}