Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: Class extends a Generic Type

Tags:

typescript

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?

like image 758
Eduardo Rosostolato Avatar asked Jul 28 '18 02:07

Eduardo Rosostolato


People also ask

Can you extend a generic class?

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.

Can generic types extend generic types?

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.

How do you define a generic type for classes in TypeScript?

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> .

Does TypeScript support generic classes?

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.


1 Answers

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

like image 98
jcalz Avatar answered Oct 02 '22 07:10

jcalz