I know it's too generic, but I wish to make a class that would have all props and prototype from a generic type like this:
class GenericExtend<T> extends T {
constructor(data: T) {
// here is a workaround to make these 2 classes unique
const proto = { ...GenericExtend.prototype };
Object.assign(proto, Object.getPrototypeOf(data));
Object.setPrototypeOf(this, proto);
Object.assign(this, data);
}
GenericMethod() { }
}
And now on, I could instanciate GenericExtend
class and then get the types of the two classes like this:
const obj = new GenericExtend<Model>(data);
obj.GenericMethod(); // ok
obj.ModelMethod(); // good!
One of my solution is to use intersection, something like this:
const obj: GenericExtend & Model = new GenericExtend(data) as any;
It worked, but I didn't like that much. Is there something better that I can do?
Generic Classes and SubtypingYou can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses.
When generic type invocation happens, the T and E in extends MyGeneric<T, E> would be replaced by type arguments which replaces type parameters T and E in Extend1<T, E> . So, in fact, both generic and non-generic types can extends/implements non-generic types only.
TypeScript supports generic classes. The generic type parameter is specified in angle brackets after the name of the class. A generic class can have generic fields (member variables) or methods. In the above example, we created a generic class named KeyValuePair with a type variable in the angle brackets <T, U> .
TypeScript fully supports generics as a way to introduce type-safety into components that accept arguments and return values whose type will be indeterminate until they are consumed later in your code.
TypeScript will not let you implement or extend another type T
unless all the keys of T
are statically known at compile time. That prevents class GenericExtend<T> implements T {...}
from being something you can write.
Instead, you have to use an intersection to get this behavior... but you can confine the type assertion to the constructor function, if you want, so that subsequent uses will not require it. Let's rename GenericExtend
out of the way:
class _GenericExtend<T> {
constructor(data: T) {
const proto = { ..._GenericExtend.prototype };
Object.assign(proto, Object.getPrototypeOf(data));
Object.setPrototypeOf(this, proto);
Object.assign(this, data);
}
GenericMethod() { }
}
And then redefine GenericExtend
as both a type and a constructor with the intersection behavior you want:
type GenericExtend<T> = _GenericExtend<T> & T;
const GenericExtend: new <T>(data: T) => GenericExtend<T> = _GenericExtend as any;
That last as any
is the type assertion we need. Now you should be able to get the behavior you want:
interface Model {
ModelMethod(): void;
}
declare const data: Model;
const obj = new GenericExtend(data);
obj.GenericMethod(); // ok
obj.ModelMethod(); // ok
Playground link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With